feat: Integrate KIND cluster management with docker-compose
KIND 클러스터를 docker-compose로 관리할 수 있도록 개선했습니다. Changes: - docker-compose.kubernetes.yml: KIND CLI 통합 관리 서비스 - kind-cli: kind, kubectl, docker 모두 포함된 통합 CLI 컨테이너 - monitor: 실시간 클러스터 모니터링 서비스 - Alpine 기반으로 자동 도구 설치 - KUBERNETES.md: docker-compose 사용법 우선으로 재구성 - 방법 1 (권장): docker-compose 명령어 - 방법 2: 로컬 스크립트 - 방법 3: 수동 설정 - KIND_README.md: 빠른 시작 가이드 신규 작성 - docker-compose 기반 간편한 사용법 - 일상적인 작업 예시 - 별칭(alias) 설정 제안 - 문제 해결 가이드 Benefits: - 간편한 관리: docker-compose 한 줄로 환경 시작 - 통합 도구: kind, kubectl, docker 모두 한 컨테이너에서 사용 - 실시간 모니터링: docker-compose logs -f monitor - 일관된 환경: 로컬에 kind/kubectl 설치 불필요 Usage: docker-compose -f docker-compose.kubernetes.yml up -d docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh setup docker-compose -f docker-compose.kubernetes.yml logs -f monitor 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
152
KIND_README.md
Normal file
152
KIND_README.md
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
# Site11 KIND Kubernetes 개발 환경
|
||||||
|
|
||||||
|
Docker Compose를 통한 간편한 로컬 Kubernetes 개발 환경
|
||||||
|
|
||||||
|
## 빠른 시작
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 관리 컨테이너 시작 (한 번만 실행)
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml up -d
|
||||||
|
|
||||||
|
# 2. KIND 클러스터 생성 및 Console 배포
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh setup
|
||||||
|
|
||||||
|
# 3. 상태 확인
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh status
|
||||||
|
```
|
||||||
|
|
||||||
|
완료! 이제 브라우저에서 접속 가능합니다:
|
||||||
|
- **Frontend**: http://localhost:3000
|
||||||
|
- **Backend**: http://localhost:8000
|
||||||
|
|
||||||
|
## 실시간 모니터링
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml logs -f monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
## 일상적인 작업
|
||||||
|
|
||||||
|
### 클러스터 상태 확인
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh status
|
||||||
|
```
|
||||||
|
|
||||||
|
### kubectl 사용
|
||||||
|
```bash
|
||||||
|
# Pod 목록
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli kubectl get pods -n site11-console
|
||||||
|
|
||||||
|
# 로그 확인
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli kubectl logs <pod-name> -n site11-console
|
||||||
|
|
||||||
|
# Shell 접속
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli kubectl exec -it <pod-name> -n site11-console -- /bin/bash
|
||||||
|
```
|
||||||
|
|
||||||
|
### 서비스 재배포
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 이미지 빌드 (로컬)
|
||||||
|
docker build -t yakenator/site11-console-backend:latest \
|
||||||
|
-f services/console/backend/Dockerfile services/console/backend
|
||||||
|
|
||||||
|
# 이미지 KIND에 로드
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli \
|
||||||
|
kind load docker-image yakenator/site11-console-backend:latest --name site11-dev
|
||||||
|
|
||||||
|
# Pod 재시작
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli \
|
||||||
|
kubectl rollout restart deployment/console-backend -n site11-console
|
||||||
|
```
|
||||||
|
|
||||||
|
## Shell 접속
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli bash
|
||||||
|
```
|
||||||
|
|
||||||
|
Shell 내에서는 `kind`, `kubectl`, `docker` 명령을 모두 사용할 수 있습니다.
|
||||||
|
|
||||||
|
## 클러스터 삭제 및 재생성
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 클러스터 삭제
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh delete
|
||||||
|
|
||||||
|
# 클러스터 재생성
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh setup
|
||||||
|
```
|
||||||
|
|
||||||
|
## 관리 컨테이너 중지
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml down
|
||||||
|
```
|
||||||
|
|
||||||
|
**참고**: 이것은 관리 헬퍼 컨테이너만 중지합니다. KIND 클러스터 자체는 계속 실행됩니다.
|
||||||
|
|
||||||
|
## 별칭(Alias) 설정 (선택사항)
|
||||||
|
|
||||||
|
`.bashrc` 또는 `.zshrc`에 추가:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
alias k8s='docker-compose -f docker-compose.kubernetes.yml'
|
||||||
|
alias k8s-exec='docker-compose -f docker-compose.kubernetes.yml exec kind-cli'
|
||||||
|
alias k8s-setup='docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh'
|
||||||
|
alias k8s-kubectl='docker-compose -f docker-compose.kubernetes.yml exec kind-cli kubectl'
|
||||||
|
```
|
||||||
|
|
||||||
|
사용 예:
|
||||||
|
```bash
|
||||||
|
k8s up -d
|
||||||
|
k8s-setup setup
|
||||||
|
k8s-setup status
|
||||||
|
k8s-kubectl get pods -A
|
||||||
|
k8s logs -f monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
## 상세 문서
|
||||||
|
|
||||||
|
더 자세한 정보는 다음 문서를 참고하세요:
|
||||||
|
- [KUBERNETES.md](./KUBERNETES.md) - 전체 가이드
|
||||||
|
- [docs/KIND_SETUP.md](./docs/KIND_SETUP.md) - KIND 상세 설정
|
||||||
|
|
||||||
|
## 문제 해결
|
||||||
|
|
||||||
|
### 클러스터가 시작되지 않는 경우
|
||||||
|
```bash
|
||||||
|
# Docker Desktop이 실행 중인지 확인
|
||||||
|
docker ps
|
||||||
|
|
||||||
|
# KIND 클러스터 상태 확인
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli kind get clusters
|
||||||
|
|
||||||
|
# 클러스터 재생성
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh delete
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh setup
|
||||||
|
```
|
||||||
|
|
||||||
|
### 이미지가 로드되지 않는 경우
|
||||||
|
```bash
|
||||||
|
# 로컬에 이미지가 있는지 확인
|
||||||
|
docker images | grep site11
|
||||||
|
|
||||||
|
# 이미지 빌드 후 다시 로드
|
||||||
|
docker build -t yakenator/site11-console-backend:latest \
|
||||||
|
-f services/console/backend/Dockerfile services/console/backend
|
||||||
|
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli \
|
||||||
|
kind load docker-image yakenator/site11-console-backend:latest --name site11-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### NodePort 접속이 안되는 경우
|
||||||
|
```bash
|
||||||
|
# 서비스 확인
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli \
|
||||||
|
kubectl get svc -n site11-console
|
||||||
|
|
||||||
|
# NodePort 확인 (30080, 30081이어야 함)
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli \
|
||||||
|
kubectl describe svc console-frontend -n site11-console
|
||||||
|
```
|
||||||
@ -26,7 +26,23 @@ brew install kubectl
|
|||||||
|
|
||||||
## 빠른 시작
|
## 빠른 시작
|
||||||
|
|
||||||
### 방법 1: 스크립트 사용 (권장)
|
### 방법 1: docker-compose 사용 (권장) ⭐
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 관리 컨테이너 시작
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml up -d
|
||||||
|
|
||||||
|
# 2. KIND 클러스터 생성 및 배포
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh setup
|
||||||
|
|
||||||
|
# 3. 상태 확인
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh status
|
||||||
|
|
||||||
|
# 4. 실시간 모니터링
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml logs -f monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
### 방법 2: 로컬 스크립트 사용
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 전체 환경 한번에 설정 (클러스터 생성 + 서비스 배포)
|
# 전체 환경 한번에 설정 (클러스터 생성 + 서비스 배포)
|
||||||
@ -39,21 +55,6 @@ brew install kubectl
|
|||||||
./scripts/kind-setup.sh access
|
./scripts/kind-setup.sh access
|
||||||
```
|
```
|
||||||
|
|
||||||
### 방법 2: docker-compose 사용
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 헬퍼 컨테이너 시작 (모니터링 포함)
|
|
||||||
docker-compose -f docker-compose.kubernetes.yml up -d
|
|
||||||
|
|
||||||
# 모니터링 로그 확인
|
|
||||||
docker-compose -f docker-compose.kubernetes.yml logs -f kind-monitor
|
|
||||||
|
|
||||||
# 헬퍼 컨테이너 중지
|
|
||||||
docker-compose -f docker-compose.kubernetes.yml down
|
|
||||||
```
|
|
||||||
|
|
||||||
**참고**: KIND 클러스터 자체는 여전히 `kind` CLI 또는 스크립트로 관리해야 합니다. docker-compose는 모니터링 및 관리 헬퍼만 제공합니다.
|
|
||||||
|
|
||||||
### 방법 3: 수동 설정
|
### 방법 3: 수동 설정
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -79,7 +80,38 @@ kubectl get pods -n site11-console
|
|||||||
|
|
||||||
## 관리 방법
|
## 관리 방법
|
||||||
|
|
||||||
### 스크립트 명령어
|
### docker-compose 명령어 (권장)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 관리 컨테이너 시작
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml up -d
|
||||||
|
|
||||||
|
# 클러스터 생성
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh create
|
||||||
|
|
||||||
|
# 클러스터 삭제
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh delete
|
||||||
|
|
||||||
|
# 전체 설정 (생성 + 배포)
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh setup
|
||||||
|
|
||||||
|
# 상태 확인
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh status
|
||||||
|
|
||||||
|
# 실시간 모니터링
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml logs -f monitor
|
||||||
|
|
||||||
|
# kubectl 직접 사용
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli kubectl get pods -A
|
||||||
|
|
||||||
|
# Shell 접속
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml exec kind-cli bash
|
||||||
|
|
||||||
|
# 관리 컨테이너 중지
|
||||||
|
docker-compose -f docker-compose.kubernetes.yml down
|
||||||
|
```
|
||||||
|
|
||||||
|
### 로컬 스크립트 명령어
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 클러스터 생성
|
# 클러스터 생성
|
||||||
|
|||||||
@ -1,72 +1,70 @@
|
|||||||
version: '3.8'
|
version: '3.8'
|
||||||
|
|
||||||
# KIND (Kubernetes IN Docker) 클러스터 관리용 docker-compose
|
# Site11 KIND Kubernetes 개발 환경
|
||||||
# 사용법:
|
#
|
||||||
# 시작: docker-compose -f docker-compose.kubernetes.yml up -d
|
# 빠른 시작:
|
||||||
# 중지: docker-compose -f docker-compose.kubernetes.yml down
|
# docker-compose -f docker-compose.kubernetes.yml up -d
|
||||||
# 재시작: docker-compose -f docker-compose.kubernetes.yml restart
|
#
|
||||||
# 로그: docker-compose -f docker-compose.kubernetes.yml logs -f
|
# 관리 명령어:
|
||||||
|
# docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh setup
|
||||||
|
# docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh status
|
||||||
|
# docker-compose -f docker-compose.kubernetes.yml logs -f monitor
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# KIND 클러스터 관리 헬퍼 서비스
|
# KIND CLI 관리 서비스 (kind, kubectl, docker 모두 포함)
|
||||||
# KIND 클러스터는 docker-compose로 직접 제어할 수 없으므로
|
kind-cli:
|
||||||
# 이 헬퍼 서비스를 통해 관리 작업을 수행합니다
|
image: alpine:latest
|
||||||
kind-manager:
|
container_name: site11-kind-cli
|
||||||
image: docker/compose:latest
|
|
||||||
container_name: site11-kind-manager
|
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- ./k8s:/k8s:ro
|
- ~/.kube:/root/.kube
|
||||||
- ./scripts:/scripts:ro
|
- ./k8s:/k8s
|
||||||
|
- ./scripts:/scripts
|
||||||
networks:
|
networks:
|
||||||
- kind
|
- kind
|
||||||
|
working_dir: /scripts
|
||||||
entrypoint: /bin/sh
|
entrypoint: /bin/sh
|
||||||
command: |
|
command: |
|
||||||
-c "
|
-c "
|
||||||
echo '====================================';
|
# Install required tools
|
||||||
echo 'Site11 KIND Cluster Manager';
|
apk add --no-cache docker-cli curl bash
|
||||||
echo '====================================';
|
|
||||||
|
# Install kubectl
|
||||||
|
curl -LO https://dl.k8s.io/release/$$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl
|
||||||
|
chmod +x kubectl && mv kubectl /usr/local/bin/
|
||||||
|
|
||||||
|
# Install kind
|
||||||
|
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64
|
||||||
|
chmod +x kind && mv kind /usr/local/bin/
|
||||||
|
|
||||||
echo '';
|
echo '';
|
||||||
echo 'KIND 클러스터 관리 명령어:';
|
echo '╔═══════════════════════════════════════╗';
|
||||||
echo ' 클러스터 생성: kind create cluster --config /k8s/kind-dev-cluster.yaml';
|
echo '║ Site11 KIND Cluster Manager ║';
|
||||||
echo ' 클러스터 삭제: kind delete cluster --name site11-dev';
|
echo '╚═══════════════════════════════════════╝';
|
||||||
echo ' 클러스터 상태: kubectl cluster-info --context kind-site11-dev';
|
|
||||||
echo ' 노드 확인: kubectl get nodes';
|
|
||||||
echo '';
|
echo '';
|
||||||
echo 'Services 배포:';
|
echo '사용 가능한 명령어:';
|
||||||
echo ' 네임스페이스: kubectl create namespace site11-console';
|
|
||||||
echo ' Console: kubectl apply -f /k8s/kind/';
|
|
||||||
echo '';
|
echo '';
|
||||||
echo '헬퍼 컨테이너는 계속 실행됩니다...';
|
echo ' 전체 설정 (클러스터 생성 + 배포):';
|
||||||
|
echo ' docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh setup';
|
||||||
|
echo '';
|
||||||
|
echo ' 개별 명령어:';
|
||||||
|
echo ' docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh create';
|
||||||
|
echo ' docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh status';
|
||||||
|
echo ' docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh delete';
|
||||||
|
echo '';
|
||||||
|
echo ' kubectl 직접 사용:';
|
||||||
|
echo ' docker-compose -f docker-compose.kubernetes.yml exec kind-cli kubectl get pods -A';
|
||||||
|
echo '';
|
||||||
|
echo ' Shell 접속:';
|
||||||
|
echo ' docker-compose -f docker-compose.kubernetes.yml exec kind-cli bash';
|
||||||
|
echo '';
|
||||||
|
echo 'KIND CLI 준비 완료!';
|
||||||
tail -f /dev/null
|
tail -f /dev/null
|
||||||
"
|
"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# Kubectl 명령어 실행을 위한 헬퍼 서비스
|
# 클러스터 실시간 모니터링
|
||||||
kubectl:
|
monitor:
|
||||||
image: bitnami/kubectl:latest
|
|
||||||
container_name: site11-kubectl
|
|
||||||
volumes:
|
|
||||||
- ~/.kube:/root/.kube:ro
|
|
||||||
- ./k8s:/k8s:ro
|
|
||||||
networks:
|
|
||||||
- kind
|
|
||||||
entrypoint: /bin/bash
|
|
||||||
command: |
|
|
||||||
-c "
|
|
||||||
echo '====================================';
|
|
||||||
echo 'Kubectl Helper Container';
|
|
||||||
echo '====================================';
|
|
||||||
echo '';
|
|
||||||
echo 'kubectl 명령어 사용 가능';
|
|
||||||
echo ' 예시: docker exec site11-kubectl kubectl get pods -A';
|
|
||||||
echo '';
|
|
||||||
tail -f /dev/null
|
|
||||||
"
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
# KIND 클러스터 헬스체크 및 모니터링
|
|
||||||
kind-monitor:
|
|
||||||
image: bitnami/kubectl:latest
|
image: bitnami/kubectl:latest
|
||||||
container_name: site11-kind-monitor
|
container_name: site11-kind-monitor
|
||||||
volumes:
|
volumes:
|
||||||
@ -77,15 +75,44 @@ services:
|
|||||||
command: |
|
command: |
|
||||||
-c "
|
-c "
|
||||||
while true; do
|
while true; do
|
||||||
echo '==== KIND Cluster Status ====';
|
clear;
|
||||||
kubectl get nodes --context kind-site11-dev 2>/dev/null || echo 'Cluster not running';
|
echo '╔═══════════════════════════════════════════════════╗';
|
||||||
|
echo '║ Site11 KIND Cluster Monitor ║';
|
||||||
|
echo '║ Updated: $$(date +"%Y-%m-%d %H:%M:%S") ║';
|
||||||
|
echo '╚═══════════════════════════════════════════════════╝';
|
||||||
echo '';
|
echo '';
|
||||||
echo '==== Console Namespace Pods ====';
|
|
||||||
kubectl get pods -n site11-console --context kind-site11-dev 2>/dev/null || echo 'Namespace not found';
|
if kubectl cluster-info --context kind-site11-dev &>/dev/null; then
|
||||||
echo '';
|
echo '✅ Cluster Status: Running';
|
||||||
echo '==== Pipeline Namespace Pods ====';
|
echo '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━';
|
||||||
kubectl get pods -n site11-pipeline --context kind-site11-dev 2>/dev/null || echo 'Namespace not found';
|
echo '';
|
||||||
|
|
||||||
|
echo '📦 Nodes:';
|
||||||
|
kubectl get nodes --context kind-site11-dev 2>/dev/null | sed '1s/.*/ &/' | sed '1!s/.*/ &/' || echo ' No nodes';
|
||||||
|
echo '';
|
||||||
|
|
||||||
|
echo '🔧 Console Namespace (site11-console):';
|
||||||
|
kubectl get pods -n site11-console --context kind-site11-dev 2>/dev/null | sed '1s/.*/ &/' | sed '1!s/.*/ &/' || echo ' No pods';
|
||||||
|
echo '';
|
||||||
|
|
||||||
|
echo '📊 Services:';
|
||||||
|
kubectl get svc -n site11-console --context kind-site11-dev 2>/dev/null | sed '1s/.*/ &/' | sed '1!s/.*/ &/' || echo ' No services';
|
||||||
|
echo '';
|
||||||
|
|
||||||
|
echo '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━';
|
||||||
|
echo '🌐 Access URLs:';
|
||||||
|
echo ' Frontend: http://localhost:3000';
|
||||||
|
echo ' Backend: http://localhost:8000';
|
||||||
|
else
|
||||||
|
echo '❌ Cluster Status: Not Running';
|
||||||
|
echo '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━';
|
||||||
|
echo '';
|
||||||
|
echo '시작 방법:';
|
||||||
|
echo ' docker-compose -f docker-compose.kubernetes.yml exec kind-cli /scripts/kind-setup.sh setup';
|
||||||
|
fi;
|
||||||
|
|
||||||
echo '';
|
echo '';
|
||||||
|
echo 'Next update in 30 seconds... (Press Ctrl+C to stop)';
|
||||||
sleep 30;
|
sleep 30;
|
||||||
done
|
done
|
||||||
"
|
"
|
||||||
|
|||||||
Reference in New Issue
Block a user