Docker swarm에 대하여4 - 8.25

양승현·2022년 8월 25일
2

docker

목록 보기
10/12

xwindow에서 cli로 전환하기

sudo systemctl set-default mult-user.target
  • 일시적으로 xwindow로 전환
startx 

cli에서 xwindow로 전환하기

sudo systemctl set-default graphical.target
  • bash / python 을 이용하여 다수의 서버를 관리할 수 있다
  • 이미 웹 서버가 설치되어 있는 상태에서..인프라 관리 툴 -> ansible ( 멱등성 : 최종 상태만을 확인하여 그 상태를 만들면 된다)
- bash : start 해라
- ansible : started 를 보장해라
- ansible 의 실체는 ssh 이다

퍼블릭 클라우드 클러스터

  • AWS, GCP에서 제공하는 컨테이너 클러스터링 서비스는 EKS, GKE가 있다.
  • 이들은 별도의 manager 용 VM을 제공해 주는 것이 아니라, 전체 관리용 VM(콘솔 VM)에 도커를 설치하여 사용한다.
  • 노드(worker)는 완전 관리형 서비스로 제공된다.
  • 서비스 생성시 별도의 네트워크를 지정하지 않는 경우 클러스터 환경에서는 ingress 네트워크에 컨테이너가 연결되고 ingress에서 제공하는 dhcp 기능을 통해 주소를 동적으로 할당 받는다.

token 동작 방식

  • manager 에서 파일에 token join 명령어를 담아서, worker node 생성시 해당 파일을 붙여넣기 하고, firstboot-command 로 해당 파일을 실행시켜, worker node 가 manager 에 자동으로 Join 되게 할 수 있다

service 배포 방식

  • replicas - 지정된 개수만큼 컨테이너를 동작시키며, 개수는 무조건 유지된다. scale 과 연계하여 확장 / 축소가 용이하다
  • global : 지정된 Node 에 각 1 개씩 global 하게 배포한다

컨테이너 비교

rapa@manager:~$ docker service create --name test1 --replicas 3 --constraint node.role!=manager -p 80:80 nginx
  • node.role!=manager 는 manager node 가 아닌 node 들을 지정하는 것이다.
  • 이는 node.role==worker 와 같은 의미다
rapa@manager:~$ docker container run -d -p 8888:80 --name test2 nginx
  • Service로 배포한 컨테이너와 container run으로 배포한 컨테이너를 비교하면 Ports 부분이 일반 컨테이너는 8888->80 과 같이 Host 의 Port 에 종속적이지만, Service 로 배포한 컨테이너는 Host 의 Port 번호가 표시되있지 않으며 Host 의 Port 에 종속적이지 않다.

--pretty 옵션

rapa@manager:~$ docker service inspect test1 --pretty 

  • inspect 할 때, pretty 옵션을 사용하면, 더 사용자 친화적으로 정보를 출력해준다.

Update

  • parallelism 은 task 한 번에 실행할 컨테이너 개수를 말한다
  • on failure 는 업데이트 진행시 오류가 발생하면 실행하는 옵션이다. pause 는 오류가 발생하면 중단시킨다.
  • continue 는 오류가 발생해도 업데이트를 계속 진행시킨다
  • 롤링 업데이트 방식으로 업데이트
rapa@manager:~$ docker service update --image gnon5367/myweb:blue test1

rollback

  • 이전 버전으로 되돌리기
rapa@manager:~$ docker service rollback test1

현업 update 방식

  • gitlab 은 저장소를 모니터링 한다. 해당 저장소에 code 가 저장되어 commit 이 변경되면, gitlab 은 commit 변경을 감지하고, 자동으로 script 를 실행한다
  • script 에는 새 이미지를 build 하여, 도커 저장소에 push 하게 설정한다
    도커 저장소에서 image 를 pull 하는데, 이때, ansible 이 명령을 manager node 에게 전달해준다. manager node 에서는 해당 명령을 받고, worker node 에게 전달한다
  • gitlab 에서 manager node 에게 명령을 바로 전달하게 Pipe Line 을 만들수도 있다
    git lab 과 jenkins 를 같이 사용할수도 있다

config/secret

  • 기존 명령에서 password를 지정하거나 변수를 선언했던 것처럼 하는것이 아니라 별도의 config 또는 secret 객체를 생성하고 이를 컨테이너에 적용하는 방식으로 운영하는 것

config

-> 레지스트리 설정파일, 변수 선언, index.html과 같은 보안성이 낮은 파일 자체
rapa@manager:~$ docker config create testconfig index.html 
rapa@manager:~$ docker config ls

rapa@manager:~$ docker config inspect testconfig

  • 데이터 값을 볼 수 있다.
rapa@manager:~$ echo "aGVsbG8gYWxsCg==" | base64 -d

[config 확인]

rapa@manager:~$ docker service create --name nginx1 --replicas 3 --constraint node.role==worker -p 8003:80 --config source=testconfig,target=/usr/share/nginx/html/index.html nginx
  • http://211.183.3.X:8003 으로 웹접속을 시도하면 config 에 적용해 두었던 "HELLO ALL" 이 보여야 한다.

secret

-> DB 패스워드, SSH Key(Pubilc key), 인증서
  • secret 사용해보기
rapa@manager:~$ echo "test1234" | docker secret create testsecret -
rapa@manager:~$ docker secret ls

  • 데이터가 보이지 않는다.
rapa@manager:~$ docker secret inspect testsecret 

  • 데이터 값을 볼 수 없다

[secret 확인]

  • secret 은 기본적으로 컨테이너의 /run/secrets 이라는 디렉터리 아래에 파일 형태로 보관된다. 또한 컨테이너내에 파일에서는 암호문이 아닌 평문으로 보관된다
rapa@manager:~$ docker service create --name sql --constraint node.role==worker --replicas 3 --secret source=testsecret,target=testsecret -e MYSQL_ROOT_PASSWORD_FILE="/run/secrets/testsecret" -e MYSQL_DATABASE=testdb mysql:5.7
rapa@worker1:~$ docker container ls
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                 NAMES
5c428b465a0f   mysql:5.7      "docker-entrypoint.s…"   13 minutes ago   Up 13 minutes   3306/tcp, 33060/tcp   sql.3.n54caefnc631migodg95u0ser
rapa@worker1:~$ docker exec 5c428b465a0f ls /run/secrets -l
total 4
-r--r--r-- 1 root root 9 Aug 25 01:58 testsecret
rapa@worker1:~$ docker exec 5c428b465a0f cat /run/secrets/testsecret
test1234

도커 네트워크

rapa@manager:~$ docker service create --name test1 --mode global nginx
rapa@manager:~$ docker service create --name test2 -p 8001:80 --mode global nginx
rapa@manager:~$ docker container ls

  • test1 상세 정보로 ip 확인
rapa@manager:~$ docker container inspect fe8317e89fa2

  • test2 상세 정보로 ip 확인
rapa@manager:~$ docker container inspect 283c77076fb4 

  • 추가적으로 overlay 네트워크를 생성한다. overlay 드라이버를 이용한 네트워크는 전 클러스터 상에 동일한 네트워크 영역을 제공하고 터널을 통해 물리적 위치와 상관없이 통신된다.
rapa@manager:~$ docker network inspect ingress 

overlay 네트워크 생성 후 서비스 배포

  • overlay 네트워크 생성
rapa@manager:~$ docker network create --subnet 20.20.20.0/24 -d overlay testoverlay
  • 네트워크 리스트 확인
rapa@manager:~$ docker network ls

  • 생성한 네트워크를 사용하여 서비스 배포
rapa@manager:~$ docker service create --name test3 -p 8002:80 --network testoverlay --mode global nginx
  • 컨테이너 id 확인
rapa@manager:~$ docker container ls | grep test3
  • test3 상세 정보로 ip 확인
rapa@manager:~$ docker container inspect 90c82198b533 

"IPAddress": "10.0.0.31",
"IPAddress": "20.20.20.6",
  • 생성한 컨테이너의 상세 정보를 확인하면, testoverlay Network 와 ingress Network 에 연결되있는 것을 확인할 수 있다. 총 2 개의 Ip 주소를 가진다
  • Swarm 환경에서 Service 배포시 -p 옵션을 사용하면, 무조건 Ingress Network 와 연결된다
  • worker Node 를 확인하면, 이제 testoverlay Network 를 확인할 수 있다. Network 생성시 생성한 Network 를 이용해 Service 를 한 번 배포한 후에 Container 가 배포된 해당 worker Node 에서 Network 를 확인할 수 있다

swarm network 구조 파악하기

  • docker container run으로 생성한 컨테이너는 --network myoverlay를 하면(클러스터 네트워크에 연결)하는 것이 불가능하다
  • 다음과 같이 overlay 네트워크를 생성할 때 --attachable을 부착하면 가능해 진다.
docker network create -d overlay --attachable myoverlay2

볼륨 사용하기

  • 볼륨 마운트 (iSCSI)
docker container run ... -v testvoiume:/var/lib/mysql 
  • 바인딩 (NFS 사용)
docker cintainer run ... -v /tetsdir:/var/www/html

호스트의 볼륨을 사용할 경우

    1. 노드 자체(호스트)에 문제가 있을 경우 볼륨 접근 불가
    1. 스케일의 축소/확장 등으로 인하여 신규 컨테이너가 기존 볼륨과 연결되지 않는 문제
    1. 각 호스트 별로 별도의 볼륨을 사용하므로 데이터의 동기화가 되지 않아 각 볼륨 별로 별도의 데이터 베이스가 동작하는 문제
  • 최종적으로 로컬 볼륨은 좋은 선택이 아니다. 외부에 있는 스토리지로 연결(사용)을 해야한다.

volume, nfs 구분

  • 서비스 생성시 볼륨 생성을 동시에 진행할 수 있다.
  • 단, 기존 컨테이너에서 -v 옵션을 이용하는 방법의 경우 volume, nfs 구분이 어려울 수 있으므로 서비스에서는 이를 명확히 구분해주어야 한다.

type=volume mount

  • type 은 volume 으로 설정하고, Host에 vol1 볼륨을 생성하여 컨테이너의 /root 디렉터리와 Mount 시켜서 컨테이너를 생성
rapa@manager:~$ docker service create --name testvol1 --mount type=volume,source=vol1,target=/root --replicas 1 --constraint node.role==manager nginx
rapa@manager:~$ docker container ls
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                                   NAMES
e39f9a76fb4d   nginx:latest   "/docker-entrypoint.…"   15 seconds ago   Up 13 seconds   80/tcp                                  testvol1.1.motsfrcqhz90tfb0rxez298ks
rapa@manager:~$ docker container exec e39f9a76fb4d df -h
Filesystem      Size  Used Avail Use% Mounted on
:
:
/dev/sda5        20G   14G  4.6G  75% /root <-- vol1 스토리지와 연결된다.
:
  • 컨테이너의 디스크 정보를 확인하자. 클러스터 환경이기에 공유되는 자원인 overlay 를 확인할 수 있다. 또한, Volume Mount 를 했으므로 Host 의 Volume 이 /dev/sda5 와 같은 디스크 형태로 /root 와 Mount 된 것을 확인할 수 있다

type=bind -> nfs

  • type 을 bind 로 하여 디렉터리와 디렉터리가 연결
rapa@manager:~$ mkdir testvol3
rapa@manager:~$ docker service create --name testvol3 --mount type=bind,source=/home/rapa/testvol3,target=/root --replicas 1 --constraint node.role==manager nginx
  • 디렉터리를 하나 만들고, Service 를 배포해주자
rapa@manager:~$ ls testvol3/
rapa@manager:~$ touch testvol3/test_host.txt
rapa@manager:~$ docker container ls | grep testvol3
71cd79204a3a   nginx:latest   "/docker-entrypoint.…"   3 minutes ago    Up 3 minutes    80/tcp                                  testvol3.1.m6qhg2108a375nioardf0ua5b
  • 컨테이너의 /root 디렉터리 안을 살펴보면 파일을 확인할 수 있다
rapa@manager:~$ docker exec 71cd79204a3a ls /root
test_from_host.txt
rapa@manager:~$ echo "hello" > testvol3/test_from_host.txt 
rapa@manager:~$ docker exec 82fff4a0922d cat /root/test_from_host.txt
hello

가용성

  • Active는 노드가 컨테이너를 동작시키고 생성할 수 있는 준비가 된 상태
  • drain - 컨테이너를 해당 노드에 배치시키지 않는다. 해당 노드에서 동작중인 모든 컨테이너는 중지
  • pause - 컨테이너를 해당 노드에 배치시키지 않는다. 컨테이너가 일시중지된다.

가용성 조정하기 - drain

  • service 배포하기
rapa@manager:~$ docker service create --name test1 --replicas 3 --constraint node.role==worker -p 8001:80 nginx
  • worker3 의 AVAILABILITY 를 drain 으로 변경
rapa@manager:~$ docker node update --availability drain worker3
worker3
  • worker3 는 drain 상태이므로 컨테이너가 중지되고, worker1,2에 하나 더 신규 생성되어 Service 를 실행한다
rapa@manager:~$ docker node ls
ID                            HOSTNAME   STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
a4mxykjw9ajjkqkx856utjg3t *   manager    Ready     Active         Leader           20.10.17
7r867xulr6u2335ih528n87z6     worker1    Ready     Active                          20.10.17
x9nd0vjcjqpuupp6fk115aifg     worker2    Ready     Active                          20.10.17
ow1jqlptdinzg14o0agv4hy4a     worker3    Ready     Drain                           20.10.17
rapa@manager:~$ docker service ps test1
ID             NAME          IMAGE          NODE      DESIRED STATE   CURRENT STATE             ERROR     PORTS
pt22h7t3t3ma   test1.1       nginx:latest   worker1   Running         Running 2 minutes ago               
urhyx2pkaa3p   test1.2       nginx:latest   worker1   Running         Running 44 seconds ago    <- 신규생성 됨. worker3 에 배치되지 않음           
63ayo6giztyx    \_ test1.2   nginx:latest   worker3   Shutdown        Shutdown 47 seconds ago             
t0ryk5aurhgf   test1.3       nginx:latest   worker2   Running         Running 2 minutes ago               
  • Scale 을 늘려서 컨테이너 늘리기
  • drain 상태인 worker3 에는 컨테이너가 배치되지 않는다
rapa@manager:~$ docker service scale test1=4  <--- scale 을 3 에서 4로 늘림
test1 scaled to 4 
overall progress: 4 out of 4 tasks 
1/4: running   
2/4: running   
3/4: running   
4/4: running   
verify: Service converged 
  • worker3 를 다시 Active 상태로 전환
rapa@manager:~$ docker node update --availability active worker3
worker3
  • Active 상태 확인
rapa@manager:~$ docker node ls | grep worker3
ow1jqlptdinzg14o0agv4hy4a     worker3    Ready     Active                          20.10.17
  • Scale 을 1 로 줄였다가 다시 3 으로 늘리면, worker3 에도 컨테이너가 배치된다.
rapa@manager:~$ docker service scale test1=1
test1 scaled to 1
overall progress: 1 out of 1 tasks 
1/1: running   
verify: Service converged 
rapa@manager:~$ docker service scale test1=3
test1 scaled to 3
overall progress: 3 out of 3 tasks 
1/3: running   
2/3: running   
3/3: running   
verify: Service converged 
rapa@manager:~$ docker service ps test1
ID             NAME          IMAGE          NODE      DESIRED STATE   CURRENT STATE            ERROR     PORTS
pt22h7t3t3ma   test1.1       nginx:latest   worker1   Running         Running 6 minutes ago              
wkn8rjhdnz0d   test1.2       nginx:latest   worker2   Running         Running 10 seconds ago             
63ayo6giztyx    \_ test1.2   nginx:latest   worker3   Shutdown        Shutdown 5 minutes ago             
6rdc8bwnt3dh   test1.3       nginx:latest   worker3   Running         Running 11 seconds ago             
  • 1,2,3 에 각 1개의 컨테이너가 재 조정 , 배치된다.

라벨 조정하기

  • label은 기존의 node.role 과 비슷하게 Node 에 key:value 형식의 label 을 부착하고, 이를 기반으로 컨테이너를 배치하고자 할 때 활용한다.
  • 노드를 구분하는 방법
node.role
node.id
node.hostname

role, id, hostname을 이용해 Node 를 구분할 수 있다.

  • role 은 다수의 Node 를 한 번에 지정할 수 있지만, id 와 hostname 같은 경우에는 Node 하나 하나 따로 지정해야 한다.
worker1 - zone-a - company: abc
worker2 - zone-b - company: abc
worker3 - zone-c - company: X
  • worker1 노드에 label 추가. --label-add
    rapa@manager:~$ docker node update --label-add zone=a --label-add company=abc worker1
    worker1 
  • worker2 노드에 label 추가
     rapa@manager:~$ docker node update --label-add zone=b --label-add company=abc worker2
    worker2
  • worker3 노드
    rapa@manager:~$ docker node update --label-add zone=c worker3
    worker3
  • worker 3 zone 상세정보
    rapa@manager:~$ docker node inspect worker3 --pretty | grep zone
    zone=c
  • zone c인 worker3에만 컨테이너 배포 하기
    rapa@manager:~$ docker service create --name zone --mode global --constraint 'node.labels.zone == c' nginx
    cdlirzcyoa03qxa7k26efjzjs
    overall progress: 1 out of 1 tasks 
    oqfoncccjqsh: running   
    verify: Service converged 
  • worker3 에만 컨테이너가 배포된다.
    rapa@manager:~$ docker service ps zone | grep zone
    7gfarybsdvbz   zone.oqfoncccjqshle8jj67omuk9f   nginx:latest   worker3   Running         Running 30 seconds ago 

swarm과 compose

swarm mode

  • 명령어를 사용하여 클러스터 환경에 컨테이너, 볼륨, 네트워크를 배포할 수 있다.
  • 단, 위의 방법을 사용할 경우 서비스 환경을 조정해야 하는 경우에는 명령을 다시 처음부터 작성해야 하는 등의 불편함이 있다.

docker-compose

  • 명령어 사용의 불편함을 해결하기 위하요 yml 형태로 환경을 구성하고 이를 도커가 명령으로 변환하여 서비스를 제공하는 기능

swarm mode + docker-compose = > docker stack

[실습] overlay - haproxy

  • 디렉터리 생성 후 yml 파일 생성

    rapa@manager:~$ mkdir 0825 ; cd 0825
    rapa@manager:~/0825$ touch web.yml
    
  • overlay 네트워크 생성

    rapa@manager:~/0825$ docker network create --driver=overlay --attachable web
    kc53oh6c5x9uhbls7u8dffxus
    
  • 네트워크 생성 확인

    rapa@manager:~/0825$ docker network ls
    NETWORK ID     NAME              DRIVER    SCOPE
    cdb3ac03134d   bridge            bridge    local
    936fa8c3abdc   docker_gwbridge   bridge    local
    3ead376f089f   host              host      local
    kc53oh6c5x9u   web               overlay   swarm
    
  • haproxy 이미지 가져오기

    rapa@manager:~/0825$ docker pull dockercloud/haproxy
    
  • web.yml 파일 구성

    rapa@manager:~/0825$ vi web.yml 
    rapa@manager:~/0825$ cat web.yml 
    version: '3.7'
    services:
      nginx: 
        image: nginx 
        deploy: 
          replicas: 3
          placement: 
            constraints: [node.role == worker] 
          restart_policy: 
            condition: on-failure
            max_attempts: 2
        environment:
          SERVICE_PORTS: 80
        networks:
          - web
    
      proxy: 
        image: dockercloud/haproxy 
        depends_on:
          - nginx 
        volumes: 
          - /var/run/docker.sock:/var/run/docker.sock
        ports:      # -p option, attached to ingress network
          - "80:80"
        networks:   # backend network -> nginx containers 
          - web
        deploy: 
          mode: global
          placement: 
            constraints: [node.role==manager]
    networks: 
      web: 
        external: true
    
  • 컨테이너 생성

    rapa@manager:~/0825$ docker stack deploy -c web.yml web
    Creating service web_nginx
    Creating service web_prox
    
  • 컨테이너 생성 확인

    rapa@manager:~/0825$ docker stack ls
    NAME      SERVICES   ORCHESTRATOR
    web       2          Swarm
    

[관리용 포테이너 설치 @ manager ]

  • 관리형 포테이너 설치
rapa@manager:~/0825$ docker container run -d --restart always -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock --name portainer portainer/portainer

Quiz.

  • HAPROXY -> manager에 배치

라벨 부착

  • worker1 -> zone=seoul
  • worker2 -> zone=seoul
  • worker3 -> zone=busan

새로운 overlay network : myovlnet -> 서브넷 : 10.10.123.0/24

haproxy는 자신의 80번 포트를 호스트의 8001과 연결하고 해당 연결은 myovlnet을 통해 zone=seoul에 배포된, wordpress로 연결된다.

최종적으로 worker3에는 cadvisor가 연결된다. 항상 worker3만 동작해야한다. container run

[풀이]

  • overlay 네트워크 생성
rapa@manager:~/0825$ docker network create --driver=overlay --subnet 10.10.123.0/24 --attachable myovlnet
nqceqh1y3hznwzori53ox7ckl
  • haproxy 이미지 가져오기
rapa@manager:~/0825$ docker pull dockercloud/haproxy
  • worker1 노드에 label 추가. --label-add
rapa@manager:~$ docker node update --label-add zone=seoul worker1
worker1 
  • worker2 노드에 label 추가
rapa@manager:~$ docker node update --label-add zone=seoul worker2
worker2
  • worker3 노드
rapa@manager:~$ docker node update --label-add zone=busan worker3
worker3
  • yml 파일 구성
rapa@manager:~/0825$ cat web.yml 
version: '3.7'

services: 
  wordpress: 
    image: wordpress 
    deploy:  
      replicas: 2
      placement: 
        constraints: [node.labels.zone == seoul] 
      restart_policy: 
        condition: on-failure 
        max_attempts: 2 
    environment: 
      SERVICE_PORTS: 80 
    networks: 
      - myovlnet  

  proxy:
    image: dockercloud/haproxy
    depends_on: 
      - wordpress
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    ports:      # -p option, attached to ingress network
      - "8001:80"
    networks:   # backend network -> nginx containers
      - myovlnet        
    deploy:
      mode: global
      placement: 
        constraints: [node.role==manager]    
networks: 
  myovlnet: 
    external: true
  • 관리형 포테이너 설치
rapa@manager:~/0825$ docker container run -d --restart always -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock --name portainer portainer/portainer

0개의 댓글