Skip to content

Latest commit

ย 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 

Repository files navigation

Kubernetes ๊ธฐ๋ฐ˜
์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐฐํฌ ๋ฐ ํŠธ๋ž˜ํ”ฝ ๋ผ์šฐํŒ… ์•„ํ‚คํ…์ฒ˜ ๊ตฌ์ถ•

image
Kubernetes ๊ธฐ๋ฐ˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐฐํฌ ํ™˜๊ฒฝ์„ ์ง์ ‘ ๊ตฌ์„ฑํ•˜๋ฉฐ, ํŠธ๋ž˜ํ”ฝ ๋ผ์šฐํŒ… ๊ตฌ์กฐ ์ „๋ฐ˜์„ ์„ค๊ณ„ยท์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
ํŠนํžˆ Service ์œ ํ˜•(ClusterIP / NodePort)์„ ํ™œ์šฉํ•ด Pod ๊ฐ„ ํ†ต์‹  ๋ฐ ์™ธ๋ถ€ ๋…ธ์ถœ ๋ฐฉ์‹์„ ๋น„๊ตยท๊ตฌ์ถ•ํ–ˆ๊ณ , Ingress ๋ฆฌ์†Œ์Šค์™€ NGINX Ingress Controller๋ฅผ ์ ์šฉํ•ด Hostname ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ… ๊ตฌ์กฐ๋ฅผ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

Kubernetes Service ์œ ํ˜•

์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์—์„œ Pod๋Š” ๋™์ ์œผ๋กœ ์ƒ์„ฑยท์‚ญ์ œ๋˜๋ฏ€๋กœ ์ง์ ‘ IP๋กœ ์ ‘๊ทผํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Service๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, ์œ ํ˜•๋ณ„ ํŠน์ง•์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๐Ÿ“ข ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ์„œ๋น„์Šค ์œ ํ˜•๋ณ„ ์„ค๋ช…

์„œ๋น„์Šค ์œ ํ˜• ๊ฐ„๋‹จํ•œ ์„ค๋ช… ์‚ฌ์šฉ ์‚ฌ๋ก€
ClusterIP ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด๋ถ€์—์„œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ IP๋ฅผ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค. ๋‚ด๋ถ€ ์„œ๋น„์Šค ๊ฐ„ ํ†ต์‹  (๊ธฐ๋ณธ ์˜ต์…˜)
NodePort ๊ฐ ๋…ธ๋“œ์˜ ํŠน์ • ํฌํŠธ๋ฅผ ์—ด์–ด ์™ธ๋ถ€์—์„œ ์„œ๋น„์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ, ๊ฐ„๋‹จํ•œ ์™ธ๋ถ€ ์ ‘๊ทผ
LoadBalancer ํด๋ผ์šฐ๋“œ ํ”Œ๋žซํผ์˜ ๋กœ๋“œ ๋ฐธ๋Ÿฐ์„œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์™ธ๋ถ€ ํŠธ๋ž˜ํ”ฝ์„ ์„œ๋น„์Šค๋กœ ๋ถ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ํด๋ผ์šฐ๋“œ ํ™˜๊ฒฝ์—์„œ ์™ธ๋ถ€ ์„œ๋น„์Šค ๋…ธ์ถœ
ExternalName ์™ธ๋ถ€ ์„œ๋น„์Šค๋ฅผ DNS CNAME ๋ ˆ์ฝ”๋“œ๋กœ ๋งคํ•‘ํ•˜์—ฌ ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด๋ถ€์—์„œ ๋ณ„์นญ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์™ธ๋ถ€ ์„œ๋น„์Šค์— ๋‚ด๋ถ€์—์„œ ์ ‘๊ทผ

๐Ÿ“ yaml ํŒŒ์ผ๋กœ ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌํ•˜๊ธฐ

  • ๋ณดํ†ต kubectl expose ๋ช…๋ น์–ด๋ฅผ ์“ฐ๋ฉด ๊ฐ„๋‹จํ•˜๊ฒŒ Service๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด kubectl์ด ์ž๋™์œผ๋กœ Service ๋ฆฌ์†Œ์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐ˜๋ฉด์— yaml ํŒŒ์ผ(nginx-clusterip.yaml)์„ ์ž‘์„ฑํ•ด์„œ kubectl apply -f๋กœ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€,
    โ†’ ๋ช…์‹œ์ ์œผ๋กœ Service ๋ฆฌ์†Œ์Šค๋ฅผ ์ •์˜ํ•˜๊ณ , ์›ํ•˜๋Š” ์„ค์ •์„ ๋ฒ„์ „ ๊ด€๋ฆฌ(Git ๋“ฑ)์™€ ํ•จ๊ป˜ ๊ธฐ๋กํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์— ๋”ฐ๋ผ, ๐Ÿ’ซ์šด์˜ ๋ฐ ์žฌํ˜„ ๊ฐ€๋Šฅํ•œ ์ธํ”„๋ผ ๊ด€๋ฆฌ ๋ฐฉ์‹์„ ์ตํžˆ๊ธฐ ์œ„ํ•ด๐Ÿ’ซ yaml ํŒŒ์ผ ๊ธฐ๋ฐ˜์˜ ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ ๋ฐฉ๋ฒ•์„ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.



๐Ÿ”ต ClusterIP ๊ตฌ์„ฑํ•˜๊ธฐ

: ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด๋ถ€์—์„œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ๋‚ด๋ถ€ IP ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. (์‚ฌ์šฉ ์˜ˆ์‹œ: ๋‚ด๋ถ€ DB, ๋‚ด๋ถ€ API ์„œ๋ฒ„)

  • ๐Ÿ“„ nginx-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-clusterip-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8900
  type: ClusterIP

๐ŸŸฃ NodePort ๊ตฌ์„ฑํ•˜๊ธฐ

: ํด๋Ÿฌ์Šคํ„ฐ ์™ธ๋ถ€์—์„œ๋„ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ฐ ๋…ธ๋“œ(Node)์˜ ํŠน์ • ํฌํŠธ์—์„œ ์„œ๋น„์Šคํ•˜๋Š” ์œ ํ˜•์ž…๋‹ˆ๋‹ค.

  • ์•„ํ‚คํ…์ฒ˜ ๊ตฌ์„ฑ๋„
image (1)
  • yaml ํŒŒ์ผ ์„ค๋ช…
    • ๋…ธ๋“œํฌํŠธ๋Š” 30000 ~ 32767์˜ ๋ฒ”์œ„๋กœ ๊ณ ์ •
    • ์™ธ๋ถ€์—์„œ ์ ‘๊ทผ ์‹œ http://: ํ˜•์‹์œผ๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅ
type: NodePort
ports:
  - port: 80
    targetPort: 8900
    nodePort: 30081
  • ๐Ÿ“„ nginx-nodeport.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: giljar-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: giljar
  template:
    metadata:
      labels:
        app: giljar
    spec:
      containers:
      - name: giljar-container
        image: byeonggill/giljar1:1.0  
        ports:
        - containerPort: 8900       
---
apiVersion: v1
kind: Service
metadata:
  name: giljar-service
spec:
  selector:
    app: giljar
  ports:
    - protocol: TCP
      port: 80        
      targetPort: 8900
      nodePort: 30081  
  type: NodePort
  • ํ•ด๋‹น ์ด๋ฏธ์ง€ ๊ตฌ๋™ ๋ฐ ์‹คํ–‰ ํ™•์ธ
์‚ฌ์ง„ ์„ค๋ช…
image yaml ํŒŒ์ผ ์ ์šฉ ๋ฐ ํ™•์ธํ•˜๊ธฐ
image curl๋กœ jar ๊ตฌ๋™ ํ™•์ธ
  • ์›น ์ ‘์† ํ™•์ธ
์‚ฌ์ง„ ์„ค๋ช…
image ์‚ฌ์ „ ์ž‘์—… : ํฌํŠธ ํฌ์›Œ๋”ฉ
image ์›น ์ ‘์†ํ•˜๊ธฐ


๐ŸŒ Ingress๋กœ Hostname ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…

์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์—์„œ Ingress๋Š” ํด๋Ÿฌ์Šคํ„ฐ ์™ธ๋ถ€์—์„œ ๋‚ด๋ถ€ ์„œ๋น„์Šค๋กœ ๋“ค์–ด์˜ค๋Š” HTTP/HTTPS ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ทœ์น™๋“ค์˜ ์ง‘ํ•ฉ์ž…๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ IP ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…์„ ์ œ๊ณตํ•˜๋Š” Service์™€ ๋‹ฌ๋ฆฌ, ๋„๋ฉ”์ธ ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Ingress๋ฅผ ํ†ตํ•ด ์™ธ๋ถ€ ํŠธ๋ž˜ํ”ฝ์„ ๋ผ์šฐํŒ…ํ•  ๋•Œ, Service๋Š” ๊ตณ์ด ์™ธ๋ถ€ IP๋ฅผ ๊ฐ€์งˆ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ํŠธ๋ž˜ํ”ฝ์€ Ingress Controller๊ฐ€ ๋ฐ›์•„์„œ ๋‚ด๋ถ€์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์™ธ๋ถ€ ๋…ธ์ถœ์„ ์ตœ์†Œํ™”ํ•˜๊ณ  ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด๋ถ€ ํ†ต์‹ ์— ์ตœ์ ํ™”๋œ ClusterIP ํƒ€์ž…์„ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ NodePort๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค๋ฉด ๋ถˆํ•„์š”ํ•œ ํฌํŠธ๊ฐ€ ์™ธ๋ถ€์— ๋…ธ์ถœ๋˜์–ด ๋ณด์•ˆ์ ์œผ๋กœ ์ทจ์•ฝํ•ด์งˆ ์ˆ˜ ์žˆ์—ˆ์„ ๊ฒ๋‹ˆ๋‹ค.

โžก๏ธ Kubernetes Ingress Service Deployment Architecture Diagram

์ฐ์ฐ์ตœ์ข… drawio

[๊ทธ๋ฆผ] Kubernetes ์š”์ฒญ ์ฒ˜๋ฆฌ ํ๋ฆ„

ํด๋ผ์ด์–ธํŠธ๊ฐ€ http://spring.local๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด Ingress๊ฐ€ ์ด๋ฅผ ๋ฐ›์•„ nginx-service๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. Service๋Š” ClusterIP(80 โ†’ 8900)์„ ํ†ตํ•ด Deployment์— ์—ฐ๊ฒฐ๋œ Pod๋“ค(spring1, spring2)๋กœ ํŠธ๋ž˜ํ”ฝ์„ ๋ถ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ ๋ผ๋ฒจ์„ ๊ฐ€์ง„ Pod๋“ค์€ ๋ชจ๋‘ containerPort: 8900์—์„œ ์š”์ฒญ์„ ์ˆ˜์‹ ํ•˜๋ฉฐ, ReplicaSet์— ์˜ํ•ด ๋‹ค์ค‘ํ™”๋˜์–ด ๋ถ€ํ•˜ ๋ถ„์‚ฐ๊ณผ ์žฅ์•  ๋ณต๊ตฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.


  • ๐Ÿ”‘ Ingress Controller
    • Ingress ๋ฆฌ์†Œ์Šค๊ฐ€ ์‹ค์ œ๋กœ ๋™์ž‘ํ•˜๋ ค๋ฉด Ingress Controller๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
    • Ingress Controller๋Š” ์ •์˜๋œ Ingress ๊ทœ์น™์„ ์ฝ๊ณ , ์‹ค์ œ๋กœ ํŠธ๋ž˜ํ”ฝ์„ ๋ผ์šฐํŒ…ํ•˜๋Š” ๋ฆฌ๋ฒ„์Šค ํ”„๋ก์‹œ(์˜ˆ: NGINX)๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“ ๊ธฐ๋Šฅ๋ณ„ yaml ํŒŒ์ผ ๊ตฌํ˜„

๐ŸŸ  Ingress ๊ตฌ์„ฑ - nginx-ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress
  namespace: default  
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: nginx.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-service # nginx.local๋กœ ๋“ค์–ด์˜จ ์š”์ฒญ์€ nginx-service์˜ port 80์œผ๋กœ ํฌ์›Œ๋”ฉ
            port:
              number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: spring-ingress
  namespace: default  
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: spring.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: spring-service # spring.local๋กœ ๋“ค์–ด์˜จ ์š”์ฒญ์€ spring-service์˜ port 88๋กœ ํฌ์›Œ๋”ฉ
            port:
              number: 88

๐ŸŸก Service ๊ตฌ์„ฑ - service.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
    - port: 80
      targetPort: 80
  type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
  name: spring-service
spec:
  selector:
    app: spring
  ports:
    - port: 88
      targetPort: 8900
  type: ClusterIP

๐ŸŸข Deployment ๊ตฌ์„ฑ - deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: registry.k8s.io/nginx:latest
        ports:
        - containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: spring
  template:
    metadata:
      labels:
        app: spring
    spec:
      containers:
      - name: spring
        image: byeonggill/giljar1:1.0   
        ports:
        - containerPort: 8900

โš™๏ธ ๋™์ž‘ ์„ค๋ช…

  • nginx

    • containerPort: 80์—์„œ HTTP ์š”์ฒญ์„ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค.
    • registry.k8s.io/nginx:latest ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    • Pod 2๊ฐœ๋กœ ์‹คํ–‰๋˜์–ด ๋ถ€ํ•˜ ๋ถ„์‚ฐ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • spring

    • containerPort: 8900์—์„œ ์š”์ฒญ์„ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค.
    • byeonggill/giljar1:1.0 ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    • Pod 2๊ฐœ๋กœ ์‹คํ–‰๋˜์–ด ๊ณ ๊ฐ€์šฉ์„ฑ์ด ๋ณด์žฅ๋ฉ๋‹ˆ๋‹ค.

โ–ถ๏ธ Ingress ์‹คํ–‰ ๋ฐฉ๋ฒ•

  • Ingress ํ™œ์„ฑํ™” ๋ฐ yaml ์‹คํ–‰
minikube addons enable ingress
kubectl apply -f nginx-ingressdepsvc.yaml 
kubectl apply -f nginx-ingress.yaml 
์‚ฌ์ง„ ์„ค๋ช…
image service, deployment, pod ํ™•์ธ
image /etc/hosts ํŒŒ์ผ์— DNS ๋“ฑ๋ก
  • ์‹คํ–‰ ํ™•์ธ
curl ํ™•์ธ health ํ™•์ธ
image image

๐Ÿ“š ํ•™์Šต ์„ฑ๊ณผ ๋ฐ ํ†ต์ฐฐ

1. ์„œ๋น„์Šค ์œ ํ˜•์˜ ์ฐจ์ด๋ฅผ ๋ช…ํ™•ํžˆ ์ดํ•ดํ–ˆ์Šต๋‹ˆ๋‹ค. ClusterIP, NodePort, Ingress๋ฅผ ์ง์ ‘ ๊ตฌ์„ฑํ•ด๋ณด๋ฉด์„œ ๋‹จ์ˆœํžˆ ๊ฐœ๋…์œผ๋กœ๋งŒ ์•Œ๊ณ  ์žˆ๋˜ ์ฐจ์ด๊ฐ€ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์ฒด๊ฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

2. ํŠธ๋ž˜ํ”ฝ ๋ผ์šฐํŒ… ๊ตฌ์กฐ๊ฐ€ ๋ˆˆ์— ๋ณด์˜€์Šต๋‹ˆ๋‹ค. Ingress๋ฅผ ์ ์šฉํ•ด nginx.local, spring.local ๊ฐ™์€ ํ˜ธ์ŠคํŠธ ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…์„ ํ…Œ์ŠคํŠธํ•˜๋ฉด์„œ, ๋‹จ์ˆœ ํฌํŠธ ๊ธฐ๋ฐ˜ ์ ‘๊ทผ๋ณด๋‹ค ํ›จ์”ฌ ์ง๊ด€์ ์ธ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด ์ธ์ƒ ๊นŠ์—ˆ์Šต๋‹ˆ๋‹ค.

3. YAML ๊ธฐ๋ฐ˜ ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ์˜ ํ•„์š”์„ฑ์„ ๋А๊ผˆ์Šต๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ kubectl expose ๊ฐ™์€ ๋ช…๋ น์œผ๋กœ ๋ฆฌ์†Œ์Šค๋ฅผ ๋งŒ๋“ค ๋•Œ๋ณด๋‹ค, kubectl apply -f . ๋ช…๋ น์–ด๋กœ 2๊ฐœ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜(์ด 4๊ฐœ Pod)์„ 5์ดˆ ์•ˆ์— ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๋Š” ์„ ์–ธํ˜• ์ธํ”„๋ผ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค. yaml ํŒŒ์ผ๋กœ ๋ช…์‹œ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์ด ์žฌํ˜„์„ฑ๊ณผ ๋ฒ„์ „ ๊ด€๋ฆฌ ์ธก๋ฉด์—์„œ ํ›จ์”ฌ ์•ˆ์ •์ ์ž„์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors