7 minute read

Docker Swarm ์‚ฌ์šฉํ•˜๊ธฐ

  1. Swarm node ๊ตฌ์„ฑ ๊ณ„ํš
  2. Swarm ๊ตฌ์„ฑํ•˜๊ธฐ
  3. Docker private registry ๊ตฌ์ถ•ํ•˜๊ธฐ
  4. Stack / Service ์‹คํ–‰
  5. Deploy๋ฅผ ์œ„ํ•œ Compose file ์ž‘์„ฑํ•˜๊ธฐ
  6. Portainer ์„ค์น˜ํ•˜๊ธฐ

1. Swarm node ๊ตฌ์„ฑ ๊ณ„ํš

ํ…Œ์ŠคํŠธ์šฉ Swarm node ๊ตฌ์„ฑ

  • book server (manager node) / 192.168.101.30
  • samsung notebook (worker node) / 192.168.100.46
  • msi notebook (worker node) / 192.168.100.47

    ํ”„๋กœ๋•ํŠธ์šฉ Swarm node ๊ตฌ์„ฑ

  • app server (manager node) / 192.168.101.80
  • api server (worker node) / 192.168.101.70
  • web server (worker node) / 192.168.101.100

์•„๋ž˜ ๋‘๊ฐ€์ง€์˜ Error ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด์ง€ ์•Š๊ธฐ ์œ„ํ•ด ํ•ด์•ผ ํ•  ์ผ

"swarm node is missing network attachments, ip addresses may be exhausted."
"failed to allocate gateway (10.0.1.1): Address already in use."
1. Hostname ์„ค์ •
$ sudo hostnamectl status
$ sudo hostnamectl set-hostname ITS-APISERVER.ITSROOM.COM
$ sudo hostnamectl set-hostname ITS-APPSERVER.ITSROOM.COM
$ sudo hostnamectl set-hostname ITS-WEBSERVER.ITSROOM.COM
$ sudo reboot
2. Docker Swarm Routing Mesh(Ingress network) ์„ค์ •
  1. Check port open
  • Port 7946 TCP/UDP for container network discovery.
  • Port 4789 UDP for the container ingress network.
# netstat -tnl
# netstat -unl
# firewall-cmd --zone=public --list-all
# firewall-cmd --zone=public --permanent --add-port=7946/tcp
# firewall-cmd --zone=public --permanent --add-port=7946/udp
# firewall-cmd --zone=public --permanent --add-port=4789/udp
# firewall-cmd --reload
# firewall-cmd --zone=public --list-all

์ฐธ์กฐ ๋ธ”๋กœ๊ทธ https://subicura.com/2017/02/25/container-orchestration-with-docker-swarm.html

2. Swarm ๊ตฌ์„ฑํ•˜๊ธฐ

docker, docker-compose ์„ค์น˜ ํ•„์ˆ˜

  • centos์— docker ์„ค์น˜ https://docs.docker.com/engine/install/centos/

    Swarm init (Swarm ๊ตฌ์ถ•): manager node์—์„œ ์‹คํ–‰

- input
[root@ITS-WEBSERVER its]# docker swarm init --advertise-addr 192.168.101.80
- output(worker node์—์„œ ์‹คํ–‰ํ•  join-token ๋ช…๋ น์–ด)
Swarm initialized: current node (j047o4z6w71zwolmvaitbkb6e) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join \
    --token SWMTKN-1-2v3sdrfzc6mkr310wqseoq1cevwkxupqldozhhzxa8jd4d8mmy-6c27bitjryr39a7wzr6j8ik1o \
    192.168.101.80:2377

- (manager node์ถ”๊ฐ€๋ฅผ ์œ„ํ•œ join-token ๋ช…๋ น์–ด)
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

node list ํ™•์ธ - node ๊ตฌ์ถ•์ด ์ž˜ ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ

- input
[root@ITS-WEBSERVER its]# docker node ls

- output
ID                            HOSTNAME                STATUS         ENGINE VERSION
nhyjv7tmnvzwe92fszb238dtm *   localhost.localdomain   Ready          19.03.8

manager node ์—์„œ ๋ฐฉํ™”๋ฒฝ ์„ค์ •

- input
[root@ITS-WEBSERVER its]# firewall-cmd --zone=public --permanent --add-port=2377/tcp
[root@ITS-WEBSERVER its]# firewall-cmd --reload
[root@ITS-WEBSERVER its]# firewall-cmd --zone=public --list-all

worker node์—์„œ join command ์‹คํ–‰ํ•˜๊ธฐ

- input
[root@ITS-WEBSERVER its]# docker swarm join --token SWMTKN-1-1nkn5ygrdfrcjoowntchb8djsh1ufski1g4805y9b8s1odgp3r-1mcdbhcg831w57bvv25u9wdoy 192.168.101.30:2377
- output
This node joined a swarm as a worker.
  • ๋ฐฉํ™”๋ฒฝ ์„ค์ •ํ•˜์ง€ ์•Š์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” Error
- output
-> error

Error response from daemon: rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: Error while dialing dial tcp 192.168.101.30:2377: connect: no route to host"

manager node์—์„œ node list ํ™•์ธ

- input
[root@ITS-WEBSERVER its]# docker node ls
- output

ID                           HOSTNAME                        STATUS  AVAILABILITY  MANAGER STATUS
063cxgv750ue99jcnxl1xljxx    its-apiserver.itsroom.com       Ready   Active
84yzzxr8fqyvi22jh9u4dlfsh    its-uyeg-webserver.itsroom.com  Ready   Active
w197c4yqd6kyyriv3p552upm8 *  ITS-WEBSERVER.ITSROOM.COM       Ready   Active        Leader

  • 80๋ฒˆ ์„œ๋ฒ„์˜ hostname์ด ์ด๋ฏธ webserver๋กœ ์„ค์ •๋˜์–ด ์žˆ์–ด 100๋ฒˆ ์„œ๋ฒ„์˜ hostname์„ it-uyeg-webserver๋กœ ์„ค์ •ํ•จ.

    tip: manager node์—์„œ join ๋ช…๋ น์–ด ๊ฒ€์ƒ‰ํ•˜๋Š” ๋ฐฉ๋ฒ•

[root@ITS-WEBSERVER its]# docker swarm join-token manager
[root@ITS-WEBSERVER its]# docker swarm join-token worker
  • Docker๋Š” ๊ณ  ๊ฐ€์šฉ์„ฑ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ํด๋Ÿฌ์Šคํ„ฐ ๋‹น 3 ๊ฐœ ๋˜๋Š” 5 ๊ฐœ์˜ ๊ด€๋ฆฌ์ž ๋…ธ๋“œ๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ์Šค์›œ ๋ชจ๋“œ ๊ด€๋ฆฌ์ž ๋…ธ๋“œ๋Š” Raft๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜๋ฏ€๋กœ ํ™€์ˆ˜์˜ ๊ด€๋ฆฌ์ž๊ฐ€ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž ๋…ธ๋“œ์˜ ์ ˆ๋ฐ˜ ์ด์ƒ์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํ•œ ์›œ์€ ๊ณ„์† ์ž‘๋™ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

tip: Node์—์„œ ์ œ๊ฑฐ

  • Worker node์—์„œ Leave Swarm ๋ช…๋ น์–ด ์‹คํ–‰
[root@ITS-WEBSERVER its]# docker swarm leave

Node left the swarm.
  • Manager node์—์„œ Remove Node ๋ช…๋ น์–ด ์‹คํ–‰
[root@ITS-WEBSERVER its]# docker node rm node-2

3. Docker private registry ๊ตฌ์ถ•ํ•˜๊ธฐ

Docker registry๋ฅผ ์œ„ํ•œ port ๋ฐฉํ™”๋ฒฝ ์„ค์ •

# firewall-cmd --zone=public --permanent --add-port=5000/tdp
# firewall-cmd --reload
# firewall-cmd --zone=public --list-all

โ€œNo such image: ~โ€ ์™€ ๊ฐ™์€ Error ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ private registry ์ƒ์„ฑ

  1. Docker registry ์„ค์น˜
    <์ฐธ์กฐ: https://novemberde.github.io/2017/04/09/Docker_Registry_0.html>

VOLUME ๊ถŒํ•œ์„ ์œ„ํ•œ ๋ช…๋ น์–ด

chmod a+rw /var/run/docker.sock
chmod 777 /home/its/data/docker (์ €์žฅ์œ„์น˜)

registry ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ (์•ˆํ•ด๋„ ์ƒ๊ด€ ์—†์Œ)

$ docker pull registry

registry๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ

$ docker run -d \
 -p 5000:5000 \
 --restart=always \
 --privileged \
 --name docker-registry \
 -e REGISTRY_STORAGE_DELETE_ENABLED=true \
 -v /home/its/data:/var/lib/registry \
 registry:2
  • docker registry์—์„œ image ์‚ญ์ œ๋ฅผ ์œ„ํ•œ ํ™˜๊ฒฝ์„ค์ • ๋ช…๋ น์–ด
 -e REGISTRY_STORAGE_DELETE_ENABLED=true
  • docker registry ์˜ volume ์„ค์ •์„ ์œ„ํ•œ ๋ช…๋ น์–ด
 -v /home/its/data:/var/lib/registry

- Registry ์— image push ํ•˜๊ธฐ pullํ•˜๊ธฐ

hello-world ์ด๋ฏธ์ง€๊ฐ€ ์—†์œผ๋‹ˆ docker hub์—์„œ pullํ•˜์ž.

$ docker pull hello-world

localhost/hello-world ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž.

$ docker tag hello-world localhost:5000/hello-world

์ด๋ฏธ์ง€ pushํ•˜๊ธฐ

$ docker push localhost:5000/hello-world

์ด๋ฏธ์ง€ ํ™•์ธํ•˜๊ธฐ

$ curl -X GET http://localhost:5000/v2/_catalog
# ์ถœ๋ ฅ {"repositories":["hello-world"]}

ํƒœ๊ทธ ์ •๋ณด ํ™•์ธํ•˜๊ธฐ

$ curl -X GET http://localhost:5000/v2/hello-world/tags/list
# ์ถœ๋ ฅ {"name":"hello-world","tags":["latest"]}

<์ฐธ์กฐ: https://waspro.tistory.com/532>

โ€œserver gave HTTP response to HTTPS clientโ€ ๋ผ๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ์ถœ๋ ฅ๋˜๋Š” ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด daemon.json ํŒŒ์ผ์„ ์ˆ˜์ •

$ vi /etc/docker/daemon.json
{
    "insecure-registries": ["192.168.101.70:5000"]
}

์žฌ์‹คํ–‰

$ systemctl daemon-reload
$ systemctl restart docker

docker registry๋กœ ๋ถ€ํ„ฐ image pull ํ•˜๊ธฐ

$ docker pull 192.168.101.70:5000/test:latest
latest: Pulling from 192.168.101.70:5000/test
68ced04f60ab: Already exists
c4039fd85dcc: Already exists
c16ce02d3d61: Already exists
7469e3cf55fe: Pull complete
Digest: sha256:335079834819530f9746329187a4c26a011ea3f74d4b607106e3d2fbd339d273
Status: Downloaded newer image for 192.168.101.70:5000/test:latest

Docker registry์—์„œ image ์‚ญ์ œํ•˜๊ธฐ

<์ฐธ์กฐ: https://stackoverflow.com/questions/25436742/how-to-delete-images-from-a-private-docker-registry>

  • to delete an image, you should run the registry container with REGISTRY_STORAGE_DELETE_ENABLED=true parameter.
  • I set โ€œdelete: enabledโ€ value to true in /etc/docker/registry/config.yml file. For this configuration no need to set REGISTRY_STORAGE_DELETE_ENABLED variable

digest ํ™•์ธ์„ ์œ„ํ•œ ๋ช…๋ น์–ด

[root@its-apiserver its]# curl -k -I -H Accept:\* http://localhost:5000/v2/test/manifests/latest
HTTP/1.1 200 OK
Content-Length: 12076
Content-Type: application/vnd.docker.distribution.manifest.v1+prettyjws
Docker-Content-Digest: sha256:e034004b3d20f3a95d0dabd48aece8b5db1eaa2bc6d9b02163e5434c7013eb0a
Docker-Distribution-Api-Version: registry/2.0
Etag: "sha256:e034004b3d20f3a95d0dabd48aece8b5db1eaa2bc6d9b02163e5434c7013eb0a"
X-Content-Type-Options: nosniff
Date: Mon, 06 Apr 2020 08:36:04 GMT

Delete ๋ช…๋ น์–ด ํ…Œ์ŠคํŠธ(์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๋ฐœ์ƒ)

[root@its-apiserver its]# curl -X DELETE http://localhost:5000/v2/test/manifests/latest
{"errors":[{"code":"DIGEST_INVALID","message":"provided digest did not match uploaded content"}]}
[root@its-apiserver its]# curl -X DELETE http://localhost:5000/v2/test/manifests/sha256:e034004b3d20f3a95d0dabd48aece8b5db1eaa2bc6d9b02163e5434c7013eb0a
{"errors":[{"code":"MANIFEST_UNKNOWN","message":"manifest unknown"}]}

docker registry๋ฅผ ์œ„ํ•œ ๊ฐ์ข… ๋ช…๋ น์–ด

01. registry ๋‚ด๋ถ€์˜ repository ์ •๋ณด ์กฐํšŒ

$ curl -X GET <REGISTRY URL:ํฌํŠธ>/v2/_catalog



02. repository ์— ๋Œ€ํ•˜์—ฌ tag ์ •๋ณด ์กฐํšŒ

$ curl -X GET <REGISTRY URL:ํฌํŠธ>/v2/<REPOSITORY ์ด๋ฆ„>/tags/list



03. content digest ์กฐํšŒ

$ curl -v --silent -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X GET http://<REGISTRY URL:ํฌํŠธ>/v2/<REPOSITORY ์ด๋ฆ„>/manifests/<TAG ์ด๋ฆ„> 2>&1 | grep Docker-Content-Digest | awk '{print ($3)}'



04. manifest ์‚ญ์ œ

$ curl -v --silent -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X DELETE http://<REGISTRY URL:ํฌํŠธ>/v2/<REPOSITORY ์ด๋ฆ„>/manifests/<DIGEST ์ •๋ณด>



05. GC(Garbage Collection)

$ docker exec -it docker-registry registry garbage-collect /etc/docker/registry/config.yml



์ถœ์ฒ˜: https://trylhc.tistory.com/entry/Docker-registry-์‚ญ์ œ-2 [์˜ค๋Š˜์ฒ˜๋Ÿผ..?]

4. Stack / Service ์‹คํ–‰

Source download

  • git clone API_Server
  1. Compose build ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด image ์ƒ์„ฑํ•˜๊ธฐ
[root@ITS-WEBSERVER its]# docker-compose build
  1. Tagging and Pushing
  • tag
[root@bookserver API_Server]# docker tag api_server_gateway 192.168.101.70:5000/api_server_gateway
[root@bookserver API_Server]# docker tag api_server_standalone 192.168.101.70:5000/api_server_standalone
[root@bookserver API_Server]# docker tag api_server_nginx2 192.168.101.70:5000/api_server_nginx2
[root@bookserver API_Server]# docker tag app_server_mobile 192.168.101.70:5000/app_server_mobile
[root@bookserver API_Server]# docker tag app_server_nginx1 192.168.101.70:5000/app_server_nginx1
[root@bookserver API_Server]# docker tag app_server_indexing 192.168.101.70:5000/app_server_indexing
[root@bookserver API_Server]# docker tag app_server_set 192.168.101.70:5000/app_server_set
[root@bookserver API_Server]# docker tag app_server_web 192.168.101.70:5000/app_server_web
  • push
[root@bookserver API_Server]# docker push 192.168.101.70:5000/api_server_gateway
[root@bookserver API_Server]# docker push 192.168.101.70:5000/api_server_standalone
[root@bookserver API_Server]# docker push 192.168.101.70:5000/api_server_nginx2
[root@bookserver API_Server]# docker push 192.168.101.70:5000/app_server_mobile
[root@bookserver API_Server]# docker push 192.168.101.70:5000/app_server_nginx1
[root@bookserver API_Server]# docker push 192.168.101.70:5000/app_server_indexing
[root@bookserver API_Server]# docker push 192.168.101.70:5000/app_server_set
[root@bookserver API_Server]# docker push 192.168.101.70:5000/app_server_web
  • image ์œ ๋ฌด ํ™•์ธ
[root@bookserver API_Server]# curl -X GET http://192.168.101.70:5000/v2/_catalog
{"repositories":["api_server_gateway","api_server_nginx_swarm","api_server_standalone","app_server_indexing","app_server_mobile","app_server_nginx_swarm","app_server_set","app_server_web"]}
  1. Deploy API Server
  • Nginx
  • Gateway (bm4server)
  • StandAlone (bm3server)
- input
[root@bookserver API_Server]# docker stack deploy --compose-file docker-compose.yml api_server
- output
Ignoring unsupported options: build, restart

Ignoring deprecated options:

container_name: Setting the container name is not supported.

Creating network api_server_backend
Creating service api_server_nginx
Creating service api_server_gateway
Creating service api_server_standalone
  • Test -Stack deploy MariaDB
[root@bookserver mariadb-docker]# docker stack deploy -c docker-compose.yml mariadb1
Creating service mariadb1_mariadb

5. Deploy๋ฅผ ์œ„ํ•œ Compose file ์ž‘์„ฑํ•˜๊ธฐ

  1. api server ymlํŒŒ์ผ ๋น„๊ต
  • ๊ธฐ์กด compose ํŒŒ์ผ
version: "3"
services:
  nginx:
    container_name: nginx

    build:
      context: ./nginx
      dockerfile: ./Dockerfile

    ports:
      - "8083:8083"

    restart: always

    networks:
      - backend

    depends_on:
      - gateway
      - standalone

  gateway:
    container_name: bm4server

    build:
      context: ./gatewayAPI
      dockerfile: ./Dockerfile

    ports:
      - "9210:9210"

    restart: always

    networks:
      - backend

  standalone:
    container_name: bm3server

    build:
      context: ./standAloneAPI
      dockerfile: ./Dockerfile

    ports:
      - "9211:9211"

    restart: always

    networks:
      - backend

networks: # ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ bridge ๋„คํŠธ์›Œํฌ
  backend:
    driver: bridge
  • deploy ์šฉ yml ํŒŒ์ผ
version: "3"
services:
  nginx2:
    container_name: nginx_swarm
    image: api_server_nginx_swarm
    build:
      context: ./nginx
      dockerfile: ./Dockerfile

    ports:
      - "8083:8083"

    restart: always

    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

    networks:
      - frontend
      - backend

    depends_on:
      - gateway
      - standalone

  gateway:
    container_name: bm4server
    image: api_server_gateway
    build:
      context: ./gatewayAPI
      dockerfile: ./Dockerfile

    ports:
      - "9210:9210"

    restart: always

    networks:
      - backend

  standalone:
    container_name: bm3server
    image: api_server_standalone
    build:
      context: ./standAloneAPI
      dockerfile: ./Dockerfile

    ports:
      - "9211:9211"

    restart: always

    networks:
      - backend

networks:
  backend:
  frontend:

6. Portainer ์„ค์น˜ํ•˜๊ธฐ

  • ref: https://help.iwinv.kr/manual/read.html?idx=548
docker volume create portainer_data

docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data --restart=always portainer/portainer

-- for gitlab server --
docker run -d -p 9009:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data --restart=always portainer/portainer

7. Registry UI ์„ค์น˜

  • https://waspro.tistory.com/532
  1. Dockerfile ์ž‘์„ฑ
[root@ITS-WEBSERVER docker-registry-web]# cat config.yaml
registry:
    # ๊ธฐ์กด์— ์„ค์น˜ํ•œ docker private registry
    url: http://192.168.101.70:5000/v2
    # Docker registry name
    name: 192.168.101.70:5000
    # docker ๊ถŒํ•œ ๋ถ€์—ฌ
    readonly: false
    auth:
    enabled: false
[root@ITS-WEBSERVER docker-registry-web]#
  1. docker-registry-web ์‹คํ–‰
[root@ITS-WEBSERVER docker-registry-web]# docker run -it -d -p 8080:8080 --name registry-web -v /home/its/docker-registry-web/config.yaml:/conf/config.yml:ro hyper/docker-registry-web
                    Unable to find image 'hyper/docker-registry-web:latest' locally
Trying to pull repository docker.io/hyper/docker-registry-web ...
latest: Pulling from docker.io/hyper/docker-registry-web
04c996abc244: Pull complete
d394d3da86fe: Pull complete
bac77aae22d4: Pull complete
b48b86b78e97: Pull complete
09b3dd842bf5: Pull complete
69f4c5394729: Pull complete
b012980650e9: Pull complete
7c7921c6fda1: Pull complete
e20331c175ea: Pull complete
40d5e82892a5: Pull complete
a414fa9c865a: Pull complete
0304ae3409f3: Pull complete
13effc1a664f: Pull complete
e5628d0e6f8c: Pull complete
0b0e130a3a52: Pull complete
d0c73ab65cd2: Pull complete
240c0b145309: Pull complete
f1fd6f874e5e: Pull complete
40b5e021928e: Pull complete
88a8c7267fbc: Pull complete
f9371a03010e: Pull complete
Digest: sha256:723ffa29aed2c51417d8bd32ac93a1cd0e7ef857a0099c1e1d7593c09f7910ae
Status: Downloaded newer image for docker.io/hyper/docker-registry-web:latest
79a564384a12020a94ce09a84497c82245706f8abb3622554b43b474d2706f71

  1. ์ ‘์†
  • http://192.168.101.80:8080/