[A-00208] Architecture入門(MicroService)
システムアーキテクチャの入門用記事になります。
とりあえずマイクロサービスの作り方について勉強したいと思います。
・簡単なマイクロサービスを作ってみる
以下のアーキテクチャをイメージしてます。

・Department Serviceの作成
最初にDBと直接やりとりするDepartment Serviceを作成したいと思います。
mysqlに事前にdepartment_dbを作成しておきます。
CREATE DATABASE department_db;
ディレクトリ構成は下記の通りです。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.ms</groupId>
<artifactId>department-svc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>department-svc</name>
<description>Department Service</description>
<properties>
<java.version>21</java.version>
<mysql.connector.version>8.4.0</mysql.connector.version>
<lombok.version>1.18.34</lombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.connector.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.example.ms.department.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "departments")
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Department {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String departmentName;
private String departmentAddress;
private String departmentCode;
}
package com.example.ms.department.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.ms.department.entity.Department;
public interface DepartmentRepository extends JpaRepository<Department, Long> {
}
package com.example.ms.department;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DepartmentSvcApplication {
public static void main(String[] args) {
SpringApplication.run(DepartmentSvcApplication.class, args);
}
}
package com.example.ms.department.service;
import com.example.ms.department.entity.Department;
public interface DepartmentService {
Department saveDepartment(Department department);
Department getDepartmentById(Long departmentId);
}
package com.example.ms.department.service.impl;
import org.springframework.stereotype.Service;
import com.example.ms.department.entity.Department;
import com.example.ms.department.repository.DepartmentRepository;
import com.example.ms.department.service.DepartmentService;
import lombok.AllArgsConstructor;
@Service
@AllArgsConstructor
public class DepartmentServiceImpl implements DepartmentService {
private final DepartmentRepository departmentRepository;
@Override
public Department saveDepartment(Department department) {
return this.departmentRepository.save(department);
}
@Override
public Department getDepartmentById(Long departmentId) {
return this.departmentRepository.findById(departmentId).get();
}
}
package com.example.ms.department.controller;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.ms.department.entity.Department;
import com.example.ms.department.service.DepartmentService;
import lombok.AllArgsConstructor;
@RestController
@RequestMapping("api/departments")
@AllArgsConstructor
public class DepartmentController {
private final DepartmentService departmentService;
@PostMapping
public ResponseEntity<Department> saveDepartment(@RequestBody Department department) {
Department savedDepartment = this.departmentService.saveDepartment(department);
return new ResponseEntity<>(savedDepartment, HttpStatus.CREATED);
}
@GetMapping("{id}")
public ResponseEntity<Department> getDepartmentById(@PathVariable("id") Long departmentId) {
Department department = this.departmentService.getDepartmentById(departmentId);
return ResponseEntity.ok(department);
}
}
spring.application.name=department-svc
# mysql connection
spring.datasource.url=jdbc:mysql://localhost:3306/department_db
spring.datasource.username=root
spring.datasource.password=password
# jpa
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update
上記を作成したら下記のコマンドで実行します。
mvn clean install
java -jar target/department-svc-0.0.1-SNAPSHOT.jar
検証はpostmanで行います。

試しにGETメソッドでデータを取得しました。問題なく動くことが確認できました。
・Docker containerで動かしてみる
作成したdepartment-serviceをdocker上で動かしてみたいと思います。オンプレミスホストにあるMysqlにコンテナにあるspringbootが接続しにいくイメージです。

dockerコンテナ内部から外部のホストにインストールされたmysqlに接続するため、下記の書き換えを行います。
spring.application.name=department-svc
# mysql connection
#spring.datasource.url=jdbc:mysql://localhost:3306/department_db
spring.datasource.url=jdbc:mysql://host.docker.internal:3306/department_db
spring.datasource.username=root
spring.datasource.password=password
# jpa
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update
下記コマンドにてイメージ作成、コンテナ実行します。
FROM openjdk:21
WORKDIR /usr/src/myapp
COPY target/department-svc-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
docker build -t department-svc .
docker run -it --detach --publish 8080:8080 --name run-department-svc department-svc
不要になったら下記のコマンドでコンテナを停止します。
docker stop run-deparment-svc
・user-serviceを作成する
次はdepartment-serviceの対になるuser-serviceを作成します。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.ms</groupId>
<artifactId>user-svc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>user-svc</name>
<description>User Service</description>
<properties>
<java.version>21</java.version>
<mysql.connector.version>8.4.0</mysql.connector.version>
<lombok.version>1.18.34</lombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.connector.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.example.ms.user.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
@Column(nullable=false, unique=true)
private String email;
private String departmentId;
}
package com.example.ms.user.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserDto {
private Long id;
private String firstName;
private String lastName;
private String email;
}
package com.example.ms.user.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DepartmentDto {
private Long id;
private String departmentName;
private String departmentAddress;
private String departmentCode;
}
package com.example.ms.user.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResponseDto {
private DepartmentDto departmentDto;
private UserDto userDto;
}
package com.example.ms.user.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.ms.user.entity.User;
public interface UserRepository extends JpaRepository<User, Long> {
}
package com.example.ms.user.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
@Configuration
public class BeanConfigurations {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public WebClient webClient() {
return WebClient.builder().build();
}
}
package com.example.ms.user.service;
import com.example.ms.user.dto.ResponseDto;
import com.example.ms.user.entity.User;
public interface UserService {
User saveUser(User user);
ResponseDto getUser(Long userId);
}
package com.example.ms.user.service.impl;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import com.example.ms.user.dto.DepartmentDto;
import com.example.ms.user.dto.ResponseDto;
import com.example.ms.user.dto.UserDto;
import com.example.ms.user.entity.User;
import com.example.ms.user.repository.UserRepository;
import com.example.ms.user.service.UserService;
import lombok.AllArgsConstructor;
@Service
@AllArgsConstructor
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
//private final RestTemplate restTemplate;
private final WebClient webClient;
@Override
public User saveUser(User user) {
return this.userRepository.save(user);
}
@Override
public ResponseDto getUser(Long userId) {
ResponseDto responseDto = new ResponseDto();
User user = this.userRepository.findById(userId).get();
UserDto userDto = mapToUser(user);
// ResponseEntity<DepartmentDto> responseEntity = this.restTemplate
// .getForEntity("http://localhost:8080/api/departments/" + user.getDepartmentId()
// , DepartmentDto.class);
DepartmentDto departmentDto = this.webClient.get()
.uri("http://localhost:8080/api/departments/" + user.getDepartmentId())
.retrieve().bodyToMono(DepartmentDto.class).block();
// DepartmentDto departmentDto = responseEntity.getBody();
// System.out.println(responseEntity.getStatusCode());
responseDto.setUserDto(userDto);
responseDto.setDepartmentDto(departmentDto);
return responseDto;
}
private UserDto mapToUser(User user) {
UserDto userDto = new UserDto();
userDto.setId(user.getId());
userDto.setFirstName(user.getFirstName());
userDto.setLastName(user.getLastName());
userDto.setEmail(user.getEmail());
return userDto;
}
}
package com.example.ms.user.controller;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.ms.user.dto.ResponseDto;
import com.example.ms.user.entity.User;
import com.example.ms.user.service.UserService;
import lombok.AllArgsConstructor;
@RestController
@RequestMapping("api/users")
@AllArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping
public ResponseEntity<User> saveUser(@RequestBody User user) {
User savedUser = this.userService.saveUser(user);
return new ResponseEntity<>(savedUser, HttpStatus.CREATED);
}
@GetMapping("{id}")
public ResponseEntity<ResponseDto> getUser(@PathVariable("id") Long userId) {
ResponseDto responseDto = this.userService.getUser(userId);
return ResponseEntity.ok(responseDto);
}
}
package com.example.ms.user;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class UserSvcApplication {
public static void main(String[] args) {
SpringApplication.run(UserSvcApplication.class, args);
}
}
spring.application.name=user-svc
# mysql
spring.datasource.url=jdbc:mysql://localhost:3306/employee_db
spring.datasource.username=root
spring.datasource.password=password
# jpa
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update
# server port
server.port=8081
上記が作成できたら下記のコマンドにて動作検証します。Department-serviceも起動しておいてください。
mvn clean install -DskipTests=true
java -jar target/user-svc-0.0.1-SNAPSHOT.jar
GETメソッドの動作検証にてDepartmentServiceと連携確認します。

・Dockerコンテナにて動かしてみる
user-serviceもdockerコンテナにて実行してみます。
イメージは下記の通りです。

まずuser-svcのimageを作成します。
application.propertiesの書き換え
spring.application.name=user-svc
# mysql
#spring.datasource.url=jdbc:mysql://localhost:3306/employee_db
spring.datasource.url=jdbc:mysql://host.docker.internal:3306/employee_db
spring.datasource.username=root
spring.datasource.password=password
# jpa
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update
# server port
server.port=8081
Dockerfileを作成します。
FROM openjdk:21
WORKDIR /usr/src/myapp
COPY target/user-svc-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8081
ENTRYPOINT ["java", "-jar", "app.jar"]
docker build -t user-svc .
とりあえず動くか確かめます。
docker run -it --detach --publish 8081:8081 --name run-user-svc user-svc
department-serviceをコンテナ化したため、うまく通信ができずにエラーとなります。
なのでdockerネットワークの構成を変更しようと思います。docker-compose.ymlを作成します。
version: '3'
services:
app1:
image: department-svc
container_name: "dep-app"
ports:
- "8080:8080"
networks:
user-department-svc-network:
ipv4_address: 172.18.0.2
app2:
image: user-svc
container_name: "usr-app"
ports:
- "8081:8081"
networks:
user-department-svc-network:
ipv4_address: 172.18.0.3
networks:
user-department-svc-network:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.18.0.0/24
docker内部ネットワークのipv4アドレスを設定します。
次にdepartment-serviceにリクエストするuser-serviceのリクエスト先をlocalhostから適切なipv4アドレスに変更します。
package com.example.ms.user.service.impl;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import com.example.ms.user.dto.DepartmentDto;
import com.example.ms.user.dto.ResponseDto;
import com.example.ms.user.dto.UserDto;
import com.example.ms.user.entity.User;
import com.example.ms.user.repository.UserRepository;
import com.example.ms.user.service.UserService;
import lombok.AllArgsConstructor;
@Service
@AllArgsConstructor
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
//private final RestTemplate restTemplate;
private final WebClient webClient;
@Override
public User saveUser(User user) {
return this.userRepository.save(user);
}
@Override
public ResponseDto getUser(Long userId) {
ResponseDto responseDto = new ResponseDto();
User user = this.userRepository.findById(userId).get();
UserDto userDto = mapToUser(user);
// ResponseEntity<DepartmentDto> responseEntity = this.restTemplate
// .getForEntity("http://localhost:8080/api/departments/" + user.getDepartmentId()
// , DepartmentDto.class);
DepartmentDto departmentDto = this.webClient.get()
//.uri("http://localhost:8080/api/departments/" + user.getDepartmentId())
.uri("http://172.18.0.2:8080/api/departments/" + user.getDepartmentId())
.retrieve().bodyToMono(DepartmentDto.class).block();
// DepartmentDto departmentDto = responseEntity.getBody();
// System.out.println(responseEntity.getStatusCode());
responseDto.setUserDto(userDto);
responseDto.setDepartmentDto(departmentDto);
return responseDto;
}
private UserDto mapToUser(User user) {
UserDto userDto = new UserDto();
userDto.setId(user.getId());
userDto.setFirstName(user.getFirstName());
userDto.setLastName(user.getLastName());
userDto.setEmail(user.getEmail());
return userDto;
}
}
上記を作成して下記のコマンドを実行します。
docker-compose build
docker-compose run
postmanで実行するとレスポンスが返ってきます。

・SpringBoot+Istio+Kubernetesでservice-mesh作成
k8s+Istio+spring-bootでservice meshを作ります。
・boot-app-1の作成
spring-bootでboot-app-1を作ります。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>service-1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>boot-app1</name>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
spring.application.name=boot-app1
package com.example.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class BootApp1Controller {
private RestTemplate restTemplate;
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@GetMapping("/service-1")
public String getValues() {
return "Getting Called by Service 1";
}
@GetMapping("/service1-2")
public String getValueFromService2() {
String val = restTemplate.getForObject("http://boot-2-app:8080/service-2", String.class);
return "Service call from service 1 to service 2 --- " + val;
}
}
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BootApp1Application {
public static void main(String[] args) {
SpringApplication.run(BootApp1Application.class, args);
}
}
上記を作成したら下記のコマンドでjarを作ります。
mvn clean install
次にDockerfileを作成します。
FROM eclipse-temurin:21
VOLUME /tmp
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app.jar"]
Dockerイメージをローカルに作っておきます。
docker build . -t myapp1/boot-app-1
下記は任意です。container起動して動くかどうかを確認します。
docker run -it <image-id>
もしくは
docker container start <container-id>
docker container logs <container-id>
docker container stop <container-id>
・Microservice関連用語集
- サービスメッシュ(Service Mesh)
- データプレーン(Data Plane) -> Envoy, HAProxy, MOSN
- コントロールプレーン(Control Plane)
- トラフィック管理 (Traffic Scheduling)
- サービスレジストリ(Service Registry)
- サービスディスカバリ(Service Discovery)
- トラフィックルーティング
- A/Bテスト
- ブルーグリーンデプロイメント
- カナリアデプロイメント
- East-west Traffic Sidecar
- トラフィックハイジャック(Istio)
- 非トラフィックハイジャック(Alibaba Mosn)
- Traffic shaping -> ネットワーク全体のトラフィックのフローを変更すること。
- Rate limiting(レート制限)
- Load Shedding(ロードシェディング)
- Traffic shifting -> トラフィックをあるロケーションから別のロケーションに移行すること。
- East-West Traffic
・Appendix
参考文献はこちら
https://medium.com/@diegogauna.developer/restful-api-using-typescript-and-react-hooks-3d99bdd0cd39
https://qiita.com/s-yoshi210/items/94812f7b378a942fd9c5
https://qiita.com/kooohei/items/0e788a2ce8c30f9dba53
https://qiita.com/studio_meowtoon/items/ef38ba2d5e913e343380
https://qiita.com/takusonix/items/c93e18b62b0adf038967
https://docs.docker.jp/engine/reference/run.html#foreground
https://aws.amazon.com/jp/builders-flash/202108/create-container-image
https://qiita.com/leafeon00000/items/e190cf92af3a487cc749
https://qiita.com/yanbou893/items/a46b37f002df8529c99c
https://qiita.com/xanadou/items/3abd3d28214dea526084
https://qiita.com/yusuke_mrmt/items/e05d7914065824384a6b
https://stackoverflow.com/questions/56582446/how-to-use-host-network-for-docker-compose
https://yoo-s.com/topic/detail/858
https://knowledge.sakura.ad.jp/23899
https://qiita.com/hoshino/items/9545d255cc0103b3d296
https://qiita.com/saitoshi/items/a931399e81e63e8e4f1e
https://qiita.com/studio_meowtoon/items/9c07e20b4124d8c5f972
https://future-architect.github.io/articles/20240620a
https://www.trendmicro.com/ja_jp/business/tech_blog/c1_container_security_221107_01.html
https://zenn.dev/tns_00/articles/docker-communicate-with-containers
コメントを残す