Linux

시작하는 엔지니어를 위해 - 3

시작하는 엔지니어를 위한 글을 쓴지 벌써 3년이 지났습니다.

3년간 저도 성장했고, 더 나은 이야기를 할수 있는 사람이 되었는지도 모르겠습니다.
그럼 이야기를 시작하겠습니다.

먼저 스레드(Thread)를 이야기하려 합니다.

스레드는 사용자가 제어할수 있는 가장 작은 단위의 리소스입니다.
스레드는 프로세스 내에서 독립적인 실행 흐름을 나타내며, 프로세스의 리소스를 공유합니다.
말로는 이해가 안될 수 있으니 한번 프로세스와 스레드를 보여드릴까 합니다.

[root@ip-172-31-37-46 ~]# ps afxuww | grep httpd
root      2908  0.0  1.4 753824 14668 ?        Ss   Sep05   7:57 /usr/sbin/httpd -DFOREGROUND
apache   24171  0.1  6.5 1121056 64932 ?       Sl   13:24   0:02  \_ /usr/sbin/httpd -DFOREGROUND
apache   24215  0.1  6.5 891616 65076 ?        Sl   13:24   0:02  \_ /usr/sbin/httpd -DFOREGROUND
apache   24226  0.1  6.1 891616 60412 ?        Sl   13:24   0:01  \_ /usr/sbin/httpd -DFOREGROUND
apache   24227  0.1  6.5 891616 64672 ?        Sl   13:24   0:01  \_ /usr/sbin/httpd -DFOREGROUND
apache   24298  0.1  6.0 891616 59792 ?        Sl   13:24   0:01  \_ /usr/sbin/httpd -DFOREGROUND
apache   24602  0.1  6.5 893756 64920 ?        Sl   13:38   0:00  \_ /usr/sbin/httpd -DFOREGROUND
apache   24603  0.1  5.9 815580 58600 ?        Sl   13:38   0:00  \_ /usr/sbin/httpd -DFOREGROUND
apache   24615  0.1  5.9 815836 58988 ?        Sl   13:38   0:00  \_ /usr/sbin/httpd -DFOREGROUND
apache   24640  0.1  6.2 815876 62040 ?        Sl   13:38   0:00  \_ /usr/sbin/httpd -DFOREGROUND
apache   24674  0.1  6.0 891360 59644 ?        Sl   13:38   0:00  \_ /usr/sbin/httpd -DFOREGROUND

.

저의 시그니처 명령어인 ps afxuwww 를 이용하여 프로세스를 확인합니다.
ps afxuwww 명령어는 프로세스리스트를 모두 트리구조로 백그라운드 프로세스도 포함해서 유저 중심으로 넓게 보여주는 명령어립니다.

부모프로세스까지 합쳐서 12개의 프로세스가 구동중입니다. 그렇다면 이프로세스에는 몇개의 쓰레드가 있는지 확인해 봅시다.

ps -eLf | grep httpd
root      2908     1  2908  0    1 Sep05 ?        00:07:57 /usr/sbin/httpd -DFOREGROUND
apache   24215  2908 24215  0    6 13:24 ?        00:00:02 /usr/sbin/httpd -DFOREGROUND
apache   24215  2908 24216  0    6 13:24 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24215  2908 24217  0    6 13:24 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24215  2908 24218  0    6 13:24 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24215  2908 24219  0    6 13:24 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24215  2908 24220  0    6 13:24 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24226  2908 24226  0    6 13:24 ?        00:00:02 /usr/sbin/httpd -DFOREGROUND
apache   24226  2908 24228  0    6 13:24 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24226  2908 24229  0    6 13:24 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24226  2908 24230  0    6 13:24 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24226  2908 24231  0    6 13:24 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24226  2908 24232  0    6 13:24 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24298  2908 24298  0    6 13:24 ?        00:00:02 /usr/sbin/httpd -DFOREGROUND
apache   24298  2908 24308  0    6 13:24 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24298  2908 24309  0    6 13:24 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24298  2908 24310  0    6 13:24 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24298  2908 24311  0    6 13:24 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24298  2908 24312  0    6 13:24 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24602  2908 24602  0    6 13:38 ?        00:00:01 /usr/sbin/httpd -DFOREGROUND
apache   24602  2908 24609  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24602  2908 24610  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24602  2908 24611  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24602  2908 24612  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24602  2908 24613  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24603  2908 24603  0    6 13:38 ?        00:00:01 /usr/sbin/httpd -DFOREGROUND
apache   24603  2908 24604  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24603  2908 24605  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24603  2908 24606  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24603  2908 24607  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24603  2908 24608  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24615  2908 24615  0    6 13:38 ?        00:00:01 /usr/sbin/httpd -DFOREGROUND
apache   24615  2908 24623  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24615  2908 24624  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24615  2908 24625  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24615  2908 24626  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24615  2908 24627  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24640  2908 24640  0    6 13:38 ?        00:00:01 /usr/sbin/httpd -DFOREGROUND
apache   24640  2908 24654  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24640  2908 24655  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24640  2908 24656  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24640  2908 24657  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24640  2908 24658  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24674  2908 24674  0    6 13:38 ?        00:00:01 /usr/sbin/httpd -DFOREGROUND
apache   24674  2908 24676  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24674  2908 24677  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24674  2908 24678  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24674  2908 24679  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24674  2908 24680  0    6 13:38 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24783  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24790  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24791  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24792  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24793  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24794  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24821  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24822  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24823  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24824  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24825  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24826  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24827  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24828  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24829  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24830  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24831  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24832  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24833  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24834  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24835  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24783  2908 24836  0   22 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24798  2908 24798  0    6 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24798  2908 24814  0    6 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24798  2908 24815  0    6 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24798  2908 24816  0    6 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24798  2908 24817  0    6 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
apache   24798  2908 24818  0    6 13:47 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND

.

총 77개의 스레드가 확인됩니다. 앞서 말했던것과 같이 스레드는 프로세스의 자원을 공유하므로 pid 가 같으면 같은 프로세스의 스레드라 할수있습니다. 그렇다면 각 스레드의 프로세스 갯수를 확인해 보겠습니다.

ps -eLf | grep httpd | awk '{print $2}' | sort -n | uniq -c
      1 2908
      6 24215
      6 24226
      6 24298
      6 24602
      6 24603
      6 24615
      6 24640
      6 24674
     22 24783
      6 24798
      1 25189

.

ps -eLf 명령어는 중요한부분은 L 옵션입니다. 스레드를 출력하는 옵션입니다. 그다음 명령어는 httpd 스레드의 2번째 행만 발라낸뒤 숫자로 정렬하고 유니크 명령어로 각 카운트를 세었습니다. 뭔가 연관성이 보이지 않나요? 6번째 행에서 보여주는 값이 프로세스내 스레드 갯수입니다.

이렇게 하나의 프로세스는 여러개의 스레드를 가지고있는 것을 확인할수 있습니다.

그럼 시작하는 엔지니어의 글을 작성한다고 했던 제가 왜 스레드니 프로세스니 하는 이야기를 하고 있을까요?

프로세스는 오늘 할 이야기의 시작이자 끝이기 때문입니다.

프로세스의 정의는 실행 중인 프로그램의 인스턴스입니다. 이는 코드, 데이터, 스택, 힙과 같은 메모리 영역, 파일 디스크립터, 환경 설정 등을 포함합니다. 운영 체제에서 기본적인 실행 단위로, 시스템 자원과 작업을 관리하는 데 사용됩니다.

이 프로세스를 격리하는 메커니즘을 Namespace 라고 합니다. 네임스페이스는 리눅스에서 프로세스를 격리하는 메커니즘입니다. 각 네임스페이스는 특정 종류의 시스템 자원을 감싸고, 프로세스가 그 자원을 별도로 보도록 합니다. 예를 들어, 네트워크, 파일시스템 마운트 포인트, 사용자 ID 등이 있습니다.

마지막으로 Cgroup 는 프로세스 그룹의 시스템 자원 사용을 제한하고 격리하는 기능을 제공합니다. cgroups를 사용하면 CPU 시간, 시스템 메모리, 네트워크 대역폭 등의 자원을 제어할 수 있습니다. 시스템 자원의 공정한 분배 및 특정 프로세스 그룹의 자원 사용을 제한하여 시스템의 안정성을 보장하는 데 사용됩니다.

Namespace, Cgroup 이 둘은 함께 사용되어 프로세스의 격리 및 자원 관리를 향상시킵니다. 네임스페이스는 격리를 제공하고, Cgroup은 자원의 사용을 제한합니다. 이러한 조합은 효과적인 리소스 관리 및 격리된 환경을 제공하는 컨테이너 기술의 기반이 됩니다.

컨테이너는 가볍고, 이동이 쉬우며, 격리되고, 관리하기 편합니다.

컨테이너는 프로세스 입니다. 호스트 OS 내에서 앞서 보여드렸던, https 프로세스와 동일하다 보면 됩니다. 그러면 docker 에서 실행 중인 httpd 프로세스를 한번 확인해 보겠습니다.

root     25830  0.3  0.8 711892  8832 ?        Sl   14:22   0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 006385b1389ce918e596adf536fedbf68fcece03b9170cd898ccc70eb79a60d2 -address /run/containerd/containerd.sock
root     25853  7.0  0.4   5860  4756 ?        Ss   14:22   0:00  \_ httpd -DFOREGROUND
33       25882  0.0  0.3 807096  3720 ?        Sl   14:22   0:00      \_ httpd -DFOREGROUND
33       25883  0.0  0.3 807096  3720 ?        Sl   14:22   0:00      \_ httpd -DFOREGROUND
33       25884  0.0  0.3 807096  3720 ?        Sl   14:22   0:00      \_ httpd -DFOREGROUND

.

Container Runtime (containerd-shim)이 실행한 httpd 컨테이너가 보입니다. 이렇게 보기엔 단순 프로세스 이지만 컨테이너가 실행한 httpd 프로세스 입니다.

앞서 보여드렸던 스레드도 비슷한 형태로 실행 중입니다.

ps -eLf | grep httpd
root     25853 25830 25853  0    1 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25882  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25940  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25941  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25942  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25943  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25944  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25945  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25946  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25947  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25948  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25949  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25950  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25951  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25952  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25953  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25954  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25955  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25956  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25957  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25958  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25959  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25960  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25961  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25962  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25963  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25964  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25882 25853 25965  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25883  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25914  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25915  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25916  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25917  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25918  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25919  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25920  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25921  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25922  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25923  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25924  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25925  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25926  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25927  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25928  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25929  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25930  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25931  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25932  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25933  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25934  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25935  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25936  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25937  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25938  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25883 25853 25939  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25884  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25887  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25888  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25889  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25890  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25891  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25892  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25893  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25894  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25895  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25896  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25897  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25898  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25899  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25900  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25901  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25902  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25903  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25904  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25905  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25906  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25907  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25908  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25909  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25910  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25911  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND
33       25884 25853 25912  0   27 14:22 ?        00:00:00 httpd -DFOREGROUND

.

이과정에서 컨테이너는 프로세스다 라는 개념을 확실하게 알았을 것이라 생각합니다.

그럼 이제 이 컨테이너의 사용을 확장해 봅시다.

docker ps
CONTAINER ID   IMAGE     COMMAND              CREATED         STATUS         PORTS     NAMES
927717d2d026   httpd     "httpd-foreground"   4 seconds ago   Up 3 seconds   80/tcp    stupefied_gagarin
182ddd3df992   httpd     "httpd-foreground"   5 seconds ago   Up 4 seconds   80/tcp    relaxed_lovelace
4d50ad075f73   httpd     "httpd-foreground"   6 seconds ago   Up 5 seconds   80/tcp    upbeat_goldwasser
cfb8c7c071b0   httpd     "httpd-foreground"   8 seconds ago   Up 7 seconds   80/tcp    crazy_chandrasekhar

.

root     26254  0.0  0.9 712212  9312 ?        Sl   14:29   0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id cfb8c7c071b06dba1e760939d07649fd1e37a23df917c50002657ca04e0c3e9b -address /run/containerd/containerd.sock
root     26276  0.7  0.4   5860  4700 ?        Ss   14:29   0:00  \_ httpd -DFOREGROUND
33       26305  0.0  0.3 807096  3664 ?        Sl   14:29   0:00      \_ httpd -DFOREGROUND
33       26306  0.0  0.3 807096  3664 ?        Sl   14:29   0:00      \_ httpd -DFOREGROUND
33       26307  0.0  0.3 807096  3664 ?        Sl   14:29   0:00      \_ httpd -DFOREGROUND
root     26408  0.0  0.9 711892  9524 ?        Sl   14:29   0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 4d50ad075f73631dfc8ae7c25130ef309857a0d4d300176f4571c5dd19fe1e9b -address /run/containerd/containerd.sock
root     26430  0.7  0.4   5860  4636 ?        Ss   14:29   0:00  \_ httpd -DFOREGROUND
33       26458  0.0  0.3 807096  3792 ?        Sl   14:29   0:00      \_ httpd -DFOREGROUND
33       26459  0.0  0.3 807096  3788 ?        Sl   14:29   0:00      \_ httpd -DFOREGROUND
33       26460  0.0  0.3 807096  3792 ?        Sl   14:29   0:00      \_ httpd -DFOREGROUND
root     26561  0.0  0.8 711892  8872 ?        Sl   14:29   0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 182ddd3df992943a4f94d8455d5f535a66c6891b7151d5c1d60409f8085c8cc5 -address /run/containerd/containerd.sock
root     26584  0.7  0.4   5860  4644 ?        Ss   14:29   0:00  \_ httpd -DFOREGROUND
33       26612  0.0  0.3 807096  3780 ?        Sl   14:29   0:00      \_ httpd -DFOREGROUND
33       26613  0.0  0.3 807096  3780 ?        Sl   14:29   0:00      \_ httpd -DFOREGROUND
33       26614  0.0  0.3 807096  3780 ?        Sl   14:29   0:00      \_ httpd -DFOREGROUND
root     26715  0.0  0.8 712148  8640 ?        Sl   14:29   0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 927717d2d02634bc6d4a31913356a2cb4aa08315ac1ed81e53d96861c46d816c -address /run/containerd/containerd.sock
root     26738  0.7  0.4   5860  4812 ?        Ss   14:29   0:00  \_ httpd -DFOREGROUND
33       26767  0.0  0.3 807096  3696 ?        Sl   14:29   0:00      \_ httpd -DFOREGROUND
33       26768  0.0  0.3 807096  3696 ?        Sl   14:29   0:00      \_ httpd -DFOREGROUND
33       26769  0.0  0.3 807096  3696 ?        Sl   14:29   0:00      \_ httpd -DFOREGROUND

.

4개의 컨테이너가 구동중이고, 각자 격리된 형태입니다. 자원만 충분하다면 컨테이너를 ulimit 에 설정된 openfile 이 받아주는 한계에서 굉장히 많은 컨테이너를 실행할수 있습니다. 컨테이너 네트워크또한 호스트와 분리한다면 더욱더 많은 컨테이너를 실행할수 있습니다.

이러한 컨테이너의 형태는 우리 엔지니어들이 입이 마르고 닳도록 말하는 kubernetes 와 연결되어있습니다.

N+1개로 이루어진 Pod는 Node에 스케줄링됩니다. 위에서 예제로 보인 4개의 컨테이너의 실행은 네번이나 run 커멘드를 실행해야 하므로 아주 귀찮습니다. Kubernetes 에서 100개의 컨테이너를 생성하는 방법은 아래와 같습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-deployment
spec:
  replicas: 100
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd:latest
        ports:
        - containerPort: 80

.

이 매니패스트를 kubectl apply -f httpd-deployment.yaml 명령어로 실행만 하면 100개의 pod가 실행됩니다.

이제 로드벨런싱을 이야기해야 할듯한데 사실 이부분도 쿠버네티스의 오브젝트들이 적절히 잘 만들어져 있기에 간단히 할수 있습니다.

apiVersion: v1
kind: Service
metadata:
  name: httpd-loadbalancer
spec:
  selector:
    app: httpd
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: LoadBalancer

.

이러한 형태로 선언만 하면 이제 끝입니다. 이런 방법으로 우리는 httpd Container를 쉽게 만들수 있습니다. 그럼 프로세스에 이어서 바로 쿠버네티스로 넘어왔는데, 뭔가 빠져있다 생각하지 않나요? 바로 노드입니다. 쿠버네티스는 컨테이너 오케스트레이션이라 불립니다. 연결된 Node에 API를 이용하여 애플리케이션과 서비스를 구성하고 관리하고 조정합니다.

드디어 오늘의 이야기의 핵심에 다가가고 있습니다.

쿠버네티스는 결국 리눅스에 설치됩니다.

우리는 Pod와 Service 오브젝트는 간단히 다룰수있지만 추상화의 너머에 있는 Kubernetes의 프로세스와 노드에서 돌고있는 Kubelet 에 대해선 간단히 다룰수 없습니다.

POD/ Service / Deployment

Control Plane (쿠버네티스 컴포넌트)

추상화된 그 너머는 파악하기도 쉽지 않습니다.

https://kubernetes.io/ko/docs/concepts/overview/components/

우리는 다양한 것들이 추상화되어있는 시대를 살고있습니다. 그래서 그 심연을 들여다 보면 멘탈이 날아가는 일이 비일비재 합니다. 이러한 일들에 내성을 가지고 이겨내기위해선 결국엔 기본능력이 오르는 수밖에 없습니다.

기본능력이라 함은 리눅스 입니다.

항상 리눅스를 잘하려면 어떻게 해야하나요? 라는 질문을 듣습니다. 리눅서로서 사실 죄송한 말이지만 리눅스를 잘하는 기준은 어디에도 없고 리눅스 서버운영에는 많은 명령어가 필요하지 않습니다. 대부분 10개이내의 명령어를 사용합니다. 하지만 대부분의 일은 그 명령어들 내에서 처리됩니다.

그렇다면 리눅스를 잘하려면 어떻게 해야하냐? 결국엔 다양한 명령어들을 경험하고 손에 익어서 문제가 발생한 시점에 그 명령어를 사용해서 시스템을 점검하는것이 리눅스를 잘하는 것입니다.

저 같은 경우엔 처음보는 시스템도 30분이면 대부분 파악이 됩니다.

그 힘은 다양한 리눅스 환경에서 헤멘탓이 큽니다.
디렉토리 구조 패키지형태 명령어 스타일 배포판 마다 조금씩 다릅니다. 이런경우 저는 레드헷 계열 리눅스를 편하게 쓰지만 처음 보는 리눅스라 해서 어려워하지 않습니다. 이유는 간단합니다.

리눅스는 프로세스를 볼수 있습니다.

앞서서 이야기한 ps 명령어가 그러합니다 ps 명령어는 실행중인 프로세스를 면밀히 관찰할수 있게하고, 실행한 프로세스의 위치 ,구조, 사용중인 자원들을 볼수 있게 합니다.

시스템의 대부분의 힌트는 프로세스 리스트에 있습니다.

시작하는 엔지니어라면 리눅스의 구조 동작을 먼저 공부하시고 그 다음엔 명령어 프로세스 동작 그리고 간단한 스크립트도 짜보는것을 추천합니다. 또한 리눅스에선 다양한 명령어 들이 있습니다. 지금 이순간에도 누군가가 리눅스의 명령어를 만들고 있을 것입니다. 자신만의 명령어를 만들어도 좋습니다. 리눅스에 익숙해지고 많이 두드려 보세요.

손가락에 익은 명령어는 시스템을 배신하지 않습니다.

사실 가끔 합니다 (rm -rf *) 과 같은..누구나 하는 실수입니다.(저는안했습니다.)

기승전리눅스를 이야기하게 되었네요.

하지만 리눅스는 현대의 IT를 이끌고있는 근간이고 대부분의 시스템이 리눅스에서 구동된다는 사실을 안다면 근본인 리눅스를 하지 않을수 없다 생각합니다.

이제 제가 하려던 이야기를 다풀어 낸것 같습니다.

오늘도 읽어주셔서 감사합니다.

좋은밤 되시라!

Argo-WorkFlow/Events

이글은 Argo WorkFlow로 CI를 하려했던 나의 경험담을 담고있다.

Argo WorkFlow를 쓰려고 결심하고 리서칭하는 중이라면 AirFlow가 있다. 돌아가라.

Argo WorkFlow는 Flow마다 Pod를 생성한다. 그대가 원하는 패턴이 맞는지 다시한번 생각하라. 매 Flow 마다 Pod가 만들어 지는것이 맞는가? 그렇다면 맞다 Argo WorkFlow 다.

그럼다면 다시 묻는다 CI를 위해서 Argo를 찾는것인가? K8S에서 다양한 CRD에 익숙하고 강력한 러닝커브는 즐거움으로 생각되고 CNCF에 기여하는게 꿈이라면 말리지 않겠다. 잘왔다. Argo WorkFlow/Events다.

이 글에선 CI를 다룬다. 물론 글이 깊진 않다. 하지만 찍먹으론 충분한 수준으로 그대에게 전달할 것이다. 먼저 Argo-WorkFlow 를 설치해야한다.

Argo 를 이용한 CI의 길은 친절하지 않다. 먼저 WorkFlow 설치는 이렇다.

https://github.com/argoproj/argo-workflows/releases/download/v3.4.5/install.yaml

다운 받는다.

https://argoproj.github.io/argo-workflows/quick-start/

에는 포트포워딩을 하는 방식이지만 우리는 Ingress 를 이용할거다. 그러기 위해선 install.yaml을 수정해야한다.

친절하게 하나하나 다설명하고 싶지만 내 의욕이 그렇게 길지 않기 때문에 대충 설명하겠다.

https://github.com/Cloud-Linuxer/Argo-test/blob/main/Argo-WorkFlow-install.yaml

이파일을 다운받던가 아래처럼 수정하자.

1257 / 1258 라인을 을 추가해야한다. 그래야 백엔드가 HTTP로 동작하고 인증모드가 서버로 동작한다 혹시 쓰고싶다면 sso 를 붙여야한다.

k create ns argo
k apply -f Argo-WorkFlow-install.yaml 
customresourcedefinition.apiextensions.k8s.io/clusterworkflowtemplates.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/cronworkflows.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/workflowartifactgctasks.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/workfloweventbindings.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/workflows.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/workflowtaskresults.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/workflowtasksets.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/workflowtemplates.argoproj.io created
serviceaccount/argo created
serviceaccount/argo-server created
role.rbac.authorization.k8s.io/argo-role created
clusterrole.rbac.authorization.k8s.io/argo-aggregate-to-admin created
clusterrole.rbac.authorization.k8s.io/argo-aggregate-to-edit created
clusterrole.rbac.authorization.k8s.io/argo-aggregate-to-view created
clusterrole.rbac.authorization.k8s.io/argo-cluster-role created
clusterrole.rbac.authorization.k8s.io/argo-server-cluster-role created
rolebinding.rbac.authorization.k8s.io/argo-binding created
clusterrolebinding.rbac.authorization.k8s.io/argo-binding created
clusterrolebinding.rbac.authorization.k8s.io/argo-server-binding created
configmap/workflow-controller-configmap created
service/argo-server created
priorityclass.scheduling.k8s.io/workflow-controller created
deployment.apps/argo-server created
deployment.apps/workflow-controller created

CRD부터 SA role 등등 마지막으로 2개의 deployment를 만든다. 그럼 내가 추가한 설정이 잘추가 됬는 지 확인하고 싶다면 argo-server 의 시작 로그를 확인한다.

k logs argo-server-5779fd7868-nb77l 
time="2023-03-21T12:56:22.411Z" level=info msg="not enabling pprof debug endpoints"
time="2023-03-21T12:56:22.411Z" level=info authModes="[server]" baseHRef=/ managedNamespace= namespace=argo secure=false ssoNamespace=argo
time="2023-03-21T12:56:22.412Z" level=warning msg="You are running in insecure mode. Learn how to enable transport layer security: https://argoproj.github.io/argo-workflows/tls/"
time="2023-03-21T12:56:22.412Z" level=warning msg="You are running without client authentication. Learn how to enable client authentication: https://argoproj.github.io/argo-workflows/argo-server-auth-mode/"
time="2023-03-21T12:56:22.412Z" level=info msg="SSO disabled"
time="2023-03-21T12:56:22.422Z" level=info msg="Starting Argo Server" instanceID= version=v3.4.5
time="2023-03-21T12:56:22.422Z" level=info msg="Creating event controller" asyncDispatch=false operationQueueSize=16 workerCount=4
time="2023-03-21T12:56:22.425Z" level=info msg="GRPC Server Max Message Size, MaxGRPCMessageSize, is set" GRPC_MESSAGE_SIZE=104857600
time="2023-03-21T12:56:22.425Z" level=info msg="Argo Server started successfully on http://localhost:2746" url="http://localhost:2746"

automode 에 [server] 이 들어가있다면 정상적으로 먹은거다. 기본이 Clinet다. 그리고 서버 시작로그에 url 에 https 가 아니라 http 면 백엔드가 http 인거다.

여기까지 하면 이제 WorkFlow를 사용할 준비가 아직 안된거다. 이건 단순히 컨트롤러와 argo-server UI까지만 설치한거다.

우리는 이제 왜 내글의 제목이 Argo-WorkFlow/Events 인지 알게 된다.

Argo 프로젝트로 CI를 하기위해선 WF만으로는 할수없다. Events 를 써야한다.

https://github.com/Cloud-Linuxer/Argo-test/blob/main/Argo-Events-install.yaml

원래 Argo-WorkFlow 와 Argo-Events 는 namespace로 분리되어있지만 나는 이 CRD의 지저분함을 견딜수없어서 argo namespace 하나로 구성했다 Argo-Events-install.yaml 파일은 namespace를 수정한 파일이다.

 k apply -f Argo-Events-install.yaml 
customresourcedefinition.apiextensions.k8s.io/eventbus.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/eventsources.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/sensors.argoproj.io created
serviceaccount/argo-events-sa created
clusterrole.rbac.authorization.k8s.io/argo-events-aggregate-to-admin created
clusterrole.rbac.authorization.k8s.io/argo-events-aggregate-to-edit created
clusterrole.rbac.authorization.k8s.io/argo-events-aggregate-to-view created
clusterrole.rbac.authorization.k8s.io/argo-events-role created
clusterrolebinding.rbac.authorization.k8s.io/argo-events-binding created
configmap/argo-events-controller-config created
deployment.apps/controller-manager created

https://github.com/Cloud-Linuxer/Argo-test/blob/main/Argo-EventsBus.yaml

이벤트 버스는 Argo-Event 를 하면서 가장 인상적인 아키텍처였다.

k apply -f Argo-EventsBus.yaml 
eventbus.argoproj.io/default created

이유는 이렇다.

 k get pod
NAME                                  READY   STATUS    RESTARTS   AGE
argo-server-5779fd7868-nb77l          0/1     Running   0          25m
controller-manager-78bbd4559b-sd28w   1/1     Running   0          3m53s
eventbus-default-stan-0               2/2     Running   0          72s
eventbus-default-stan-1               2/2     Running   0          63s
eventbus-default-stan-2               2/2     Running   0          54s
workflow-controller-5f7f4d8-96bnm     1/1     Running   0          25m

세개의 이벤트 버스가 pod 로 뜨고,

https://argoproj.github.io/argo-events/concepts/architecture/

event source와 sonser 사이에서 버스역할을 하는 pod 가 있는것이었다.

여기까지 왔다면, 이제 Argo-WorkFlow/Events 를 사용할 준비가 완료된것이다.

그럼 한번 Ingress 를 배포해보자

https://github.com/Cloud-Linuxer/Argo-test/blob/main/Argo-Server-Ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: argo-ingress
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]'
    alb.ingress.kubernetes.io/security-groups: sg-0cd215a1ea38d94bf
    alb.ingress.kubernetes.io/subnets: subnet-0b00bab5bde81c736,subnet-0928ee0c6eaaecea2
    alb.ingress.kubernetes.io/backend-protocol: HTTP
    alb.ingress.kubernetes.io/healthcheck-protocol: HTTP
    alb.ingress.kubernetes.io/healthcheck-path: /
spec:
  ingressClassName: alb
  rules:
    - http:
        paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: argo-server
              port:
                number: 2746

어차피 보안그룹 서브넷만 수정해서 넣자. 바로된다. 만약안되면 pod가 존재하는 node의 보안그룹에 2746포트를 열지않아서 그렇다. 열어주자.

그러면 꼴뚜기 친구를 볼수있다. 이제 WorkFlow UI 까지 띄우고 Events 를 사용할 준비가 마무리 된거다.

git webhook을 이용한 간단한 테스트 정도만 이어 갈거다 걱정하지 마라. 먼저 알아야 할것은 workflow / Events 라는 놈은 CRD를 떡칠해 놨기에 우리가 생각하는 K8S의 컴포넌트 동작과는 다르다. K8S의 컴포넌트를 이용하긴 하나, 받아서 던져주는 EventBus 같은녀석도 있기때문에 Ingress 가 정상적으로 동작해도 Bus를 탈수없는 경우도 있다.

그럼 Events 로 가기전에 CRD의 RBAC를 설치해야 한다.

 # sensor rbac
k apply -f https://raw.githubusercontent.com/argoproj/argo-events/master/examples/rbac/sensor-rbac.yaml
 # workflow rbac
k apply -f https://raw.githubusercontent.com/argoproj/argo-events/master/examples/rbac/workflow-rbac.yaml

나는 이 RBAC를 적용하면서 사실 살짝 현타가 왔다. CRD의 모든 동작을 알순없더라도 적어도 내가 통제할수는 있는 레벨이어야 하는데 너무 쪼개진 컴포넌트 들이 나를 힘들게 했다.

자 그럼 이제 이벤트소스-웹훅-센서-트리거 를 배포해 보자.

사실 엄청 거창해 보이지만 yaml 로는 두개다.

https://github.com/Cloud-Linuxer/Argo-test/blob/main/Argo-EventSource.yaml

apiVersion: argoproj.io/v1alpha1
kind: EventSource
metadata:
  name: webhook
spec:
  service:
    ports:
      - port: 12000
        targetPort: 12000
  webhook:
    example:
      port: "12000"
      endpoint: /
      method: POST

EventSource 의 yaml 은 하나지만 CRD라 Service 와 Pod를 배포해준다.

 k get all -l eventsource-name=webhook
NAME                                             READY   STATUS    RESTARTS   AGE
pod/webhook-eventsource-r7x58-8575c749bb-fsjnf   1/1     Running   0          6m

NAME                              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)     AGE
service/webhook-eventsource-svc   ClusterIP   100.64.116.226   <none>        12000/TCP   6m

k get eventsources.argoproj.io 
NAME      AGE
webhook   10m

eventsource-name=webhook label 이 붙는다. owner나 이런 저런것도 붙는다.

그럼 이제 Sonser를 배포할거다. 이벤트소스를 통해 전달받은 웹훅을 센서가 받아서 트리거를 호출하고 실행한다.

https://github.com/Cloud-Linuxer/Argo-test/blob/main/Argo-Sensor-Webhook.yaml

k apply -f Argo-Sensor-Webhook.yaml 
sensor.argoproj.io/webhook created

배포가 완료되면

이렇게 Argo WorkFlow UI에서 확인할수 있다.
그럼 웹훅까지 발생시켜 보자.

웹훅도 Ingress 로 받을 거다.

k get svc
NAME                        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
argo-server                 ClusterIP   100.68.95.174    <none>        2746/TCP                     37m
eventbus-default-stan-svc   ClusterIP   None             <none>        4222/TCP,6222/TCP,8222/TCP   64m
webhook-eventsource-svc     ClusterIP   100.64.116.226   <none>        12000/TCP                    14m

Service를 보면 webhook-eventsource-svc Eventsource 가 있다. 이걸 Ingress 로 연결해야한다. Ingress 를 생성하면 이런식으로 두개의 Ingress 가 생긴다.

k get ingress
NAME                       CLASS   HOSTS   ADDRESS                                                                    PORTS   AGE
argo-eventsource-ingress   alb     *       k8s-argo-argoeven-44fe46d880-57919429.ap-northeast-2.elb.amazonaws.com     80      26s
argo-ingress               alb     *       k8s-argo-argoingr-17062136f1-1802335500.ap-northeast-2.elb.amazonaws.com   80      44m

그럼 argo-eventsource-ingress 쪽으로 웹훅을 날려보자.

curl -d '{"message":"this is my first webhook"}' -H "Content-Type: application/json" -X POST http://k8s-argo-argoeven-44fe46d880-57919429.ap-northeast-2.elb.amazonaws.com
success

success가 떨어질것이다. 그럼 정상적으로 실행됬는지 UI에서 확인해보자.

WorkFlow가 생성된게 보인다. 그럼 이걸 CLI 에서도 확인해보자.
k get wf
NAME            STATUS      AGE   MESSAGE
webhook-9vzcz   Succeeded   88s   

정상적으로 완료됬다. 그럼 대량의 웹훅을 날린다면???

헤헤 주거라 WorkFlow!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

대량으로 생성된걸 확인할수 있다. 이때 pod는?

k get pod | grep webhook
webhook-4j9gq                                0/2     Completed   0          108s
webhook-4q646                                0/2     Completed   0          101s
webhook-6k44h                                0/2     Completed   0          96s
webhook-96w4x                                0/2     Completed   0          106s
webhook-9vzcz                                0/2     Completed   0          3m58s
webhook-blmsq                                0/2     Completed   0          107s
webhook-eventsource-r7x58-8575c749bb-fsjnf   1/1     Running     0          26m
webhook-fq6d5                                0/2     Completed   0          102s
webhook-fzh2t                                0/2     Completed   0          103s
webhook-g58r9                                0/2     Completed   0          103s
webhook-gk9wb                                0/2     Completed   0          98s
webhook-mh6b2                                0/2     Completed   0          104s
webhook-mm9qx                                0/2     Completed   0          105s
webhook-n9x4k                                0/2     Completed   0          106s
webhook-nhd8l                                0/2     Completed   0          109s
webhook-ps8z6                                0/2     Completed   0          109s
webhook-qnnbd                                0/2     Completed   0          100s
webhook-qrm8d                                0/2     Completed   0          97s
webhook-rcztl                                0/2     Completed   0          98s
webhook-sensor-lqv7w-6459d75dbb-xlkh8        1/1     Running     0          13m
webhook-vs2tm                                0/2     Completed   0          99s
webhook-xtfbh                                0/2     Completed   0          104s
webhook-z6twq                                0/2     Completed   0          108s

늘어난다.

나는 이다음 github webhook과 인증인가 Ingress 보호등 다양한 부분을 확인하고 테스트했다. 그이후엔 셀프로 이걸 관리하면서 사용할거라면 안하는게 맞다는 결론을 내렸다.

여러분도 Workflow를 적용하려 한다면 고충이 클것이다.

이포스팅이 여러분의 앞날에 삽질을 줄여주길 바란다. 이만!

PKOS-kOps-1Week

이번에 스터디에 참가하게 되었다.

가시다님의 PKOS!

스터디할시에 사용하는 책은 24단계 실습으로 정복하는 쿠버네티스 이다.

kOps를 프로비저닝하는데 오타가 발생해서 심심해서 스크립트를 만들었다.
그덕에 한번 다시 만들었다.

#!/bin/bash

echo "클러스터명-도메인을 입력해주세요 : "
read KOPS_CLUSTER_NAME
echo "버킷명을 입력해 주세요 s3:// 는 입력하지 않아도 됩니다. : "
read  KOPS_STATE_STORE
# Access Key를 입력 받음
read -p "엑세스키를 입력해주세요 : " ACCESS_KEY

# Secret Access Key를 입력 받음
read -p "시크릿키를 입력해주세요 : " SECRET_KEY

# AWS 계정 구성
aws configure set aws_access_key_id $ACCESS_KEY
aws configure set aws_secret_access_key $SECRET_KEY
echo 'export AWS_PAGER=""' >>~/.bashrc
echo "export REGION=ap-northeast-2" >>~/.bashrc
echo "export KOPS_CLUSTER_NAME=$KOPS_CLUSTER_NAME" >>~/.bashrc
echo "export KOPS_STATE_STORE=s3://$KOPS_STATE_STORE" >>~/.bashrc

kops create cluster --zones="$REGION"a,"$REGION"c --networking amazonvpc --cloud aws \
--master-size t3.medium --node-size t3.medium --node-count=2 --network-cidr 172.30.0.0/16 \
--ssh-public-key ~/.ssh/id_rsa.pub --name=$KOPS_CLUSTER_NAME --kubernetes-version "1.24.10" --dry-run -o yaml > mykops.yaml


kops create cluster --zones="$REGION"a,"$REGION"c --networking amazonvpc --cloud aws \
--master-size t3.medium --node-size t3.medium --node-count=2 --network-cidr 172.30.0.0/16 \
--ssh-public-key ~/.ssh/id_rsa.pub --name=$KOPS_CLUSTER_NAME --kubernetes-version "1.24.10" -y

source <(kubectl completion bash)
echo 'source <(kubectl completion bash)' >> ~/.bashrc
echo 'alias k=kubectl' >> ~/.bashrc
echo 'complete -F __start_kubectl k' >> ~/.bashrc

read 명령어를 이용하여 스크립트에 변수를 부여하고 입력받은 변수를 이용하여 aws configure 를 설정하고, kops 명령어로 k8s 클러스터를 프로비저닝한다.

이거다음에는 사실 initscript 에 내가원하는 값을 넣는게 제일 편하나 그건..좀 공개하기 애매하니 스크립트라도 공개한다.

DOIK-Study

가시다님과 함께하는 스터디는 항상 즐겁다. 이번 스터디엔 라이브로 못해서..일단 바닐라쿠버 배포하고 시작했다.

Headless 서비스는 ClusterIP가 None으로 설정하며 kubeproxy를 통하지않고 pod의 endpoint가 svc에 연결된다.

나는 먼저 NFS 서버를 Headless 로 배포하기로 했다.

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: nfs-server-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
      
---

kind: Service
apiVersion: v1
metadata:
  name: nfs-service
spec:
  type: ClusterIP
  clusterIP: None
  selector:
    role: nfs
  ports:
    # Open the ports required by the NFS server
    # Port 2049 for TCP
    - name: tcp-2049
      port: 2049
      protocol: TCP

    # Port 111 for UDP
    - name: udp-111
      port: 111
      protocol: UDP
      
    # Port 20048 for TCP
    - name: tcp-20048
      port: 20048
      protocol: TCP
---

apiVersion: v1
kind: ReplicationController
metadata:
  name: nfs-server
spec:
  replicas: 1
  selector:
    role: nfs-server
  template:
    metadata:
      labels:
        role: nfs-server
    spec:
      containers:
      - name: nfs-server
        image: gcr.io/google_containers/volume-nfs:0.8
        ports:
          - name: nfs
            containerPort: 2049
          - name: mountd
            containerPort: 20048
          - name: rpcbind
            containerPort: 111
        securityContext:
          privileged: true
        volumeMounts:
          - mountPath: /exports
            name: nfs-export
      volumes:
        - name: nfs-export-fast
          persistentVolumeClaim:
            claimName: nfs-server-pvc-fast
            

🐤

yaml을 deploy 하면 다음과같다.

이제 프로비저너 셋팅이 좀 필요하다. 가시다님께서는 친절하게 프로비저너 셋팅도 다해주셨지만 나는 내가만든 NFS 서버를 사용할거기 때문에 프로비저너를 다시 배포할거다.

#지우고
helm delete -n kube-system nfs-provisioner
#다시 설치하고
helm install nfs-provisioner -n kube-system nfs-subdir-external-provisioner/nfs-subdir-external-provisioner --set nfs.server=nfs-service.default.svc.cluster.local --set nfs.path=/exports
NAME: nfs-provisioner
LAST DEPLOYED: Thu May 26 16:10:31 2022
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None

🐤

응 잘됬다.

(🐤 |DOIK-Lab:default) root@k8s-m:~# k get svc
NAME          TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)            AGE
kubernetes    ClusterIP   10.200.1.1   <none>        443/TCP            4h30m
nfs-service   ClusterIP   None         <none>        2049/TCP,111/UDP   6m29s

(🐤 |DOIK-Lab:default) root@k8s-m:~# k get ep
NAME          ENDPOINTS                            AGE
kubernetes    192.168.10.10:6443                   4h30m
nfs-service   172.16.158.2:2049,172.16.158.2:111   6m48s

(🐤 |DOIK-Lab:default) root@k8s-m:~# k get pod -o wide
NAME             READY   STATUS    RESTARTS   AGE    IP             NODE     NOMINATED NODE   READINESS GATES
nfs-server-pod   1/1     Running   0          7m1s   172.16.158.2   k8s-w1   <none>           <none>

🐤

정상적으로 NFS서버가 잘 배포된것을 확인할수 있다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-nfs-pv
  labels:
    type: mysql-nfs-pv
spec:
  storageClassName: nfs-client
  capacity:
    storage: 4Gi
  accessModes:
  - ReadWriteOnce          # ReadWriteOnce RWO (1:1 마운트, 읽기 쓰기)
  nfs:
    server: 172.16.184.9 # NFS-Server 의 IP
    path: /1       # NFS 저장소
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: wp-nfs-pv
  labels:
    type: wp-nfs-pv
spec:
  storageClassName: nfs-client
  capacity:
    storage: 4Gi
  accessModes:
  - ReadWriteOnce
  nfs:
    server: 172.16.184.9 # NFS-Server 의 IP
    path: /2      # NFS 저장소

🐤

nfs-service.svc.cluster.local domain을 이용하려 하였으나, PV 에서 domain으로 설정시 nfs-provisioner 정상적으로 마운트 되지 않았다.

headless NFS를 하려고 한것이나, 실패하였다.
지원하지 않는다.(결론)

다음과 같은 증상이었다. IP로 프로비저너 설치후엔 잘되었다.

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  labels:
    app: wordpress
  name: mysql-pv-claim
spec:
  storageClassName: nfs-client
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 4Gi
  selector:
    matchLabels:
      type: "mysql-nfs-pv"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  labels:
    app: wordpress
  name: wp-pv-claim
spec:
  storageClassName: nfs-client
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 4Gi
  selector:
    matchLabels:
      type: "wp-nfs-pv"

🐤

selector를 이용하여 PV를 사용하도록 설정해 주었다

Every 2.0s: kubectl get svc,pods,pv,pvc -o wide                                                                                                                                                                    k8s-m: Thu May 26 18:10:41 2022

NAME                      TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)                      AGE     SELECTOR
service/kubernetes        ClusterIP   10.200.1.1    <none>        443/TCP                      7h56m   <none>
service/nfs-service       ClusterIP   None          <none>        2049/TCP,111/UDP,20048/TCP   71m     role=nfs
service/wordpress         NodePort    10.200.1.33   <none>        80:30387/TCP                 3m25s   app=wordpress,tier=frontend
service/wordpress-mysql   ClusterIP   None          <none>        3306/TCP                     3m25s   app=wordpress,tier=mysql

NAME                                   READY   STATUS    RESTARTS   AGE     IP              NODE     NOMINATED NODE   READINESS GATES
pod/nfs-server-rxvf7                   1/1     Running   0          71m     172.16.184.9    k8s-w2   <none>           <none>
pod/wordpress-859f989bbb-msppd         1/1     Running   0          3m25s   172.16.158.21   k8s-w1   <none>           <none>
pod/wordpress-mysql-66fb7cfb68-z9vj5   1/1     Running   0          3m25s   172.16.158.20   k8s-w1   <none>           <none>

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                         STORAGECLASS   REASON   AGE     VOLUMEMODE
persistentvolume/mysql-nfs-pv                               4Gi        RWO            Retain           Bound    default/mysql-pv-claim        nfs-client              3m32s   Filesystem
persistentvolume/pvc-adc24c97-ca67-4700-b3c5-2fc51c4cce01   10Gi       RWO            Delete           Bound    default/nfs-server-pvc-fast   local-path              71m     Filesystem
persistentvolume/wp-nfs-pv                                  4Gi        RWO            Retain           Bound    default/wp-pv-claim           nfs-client              3m32s   Filesystem

NAME                                        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE     VOLUMEMODE
persistentvolumeclaim/mysql-pv-claim        Bound    mysql-nfs-pv                               4Gi        RWO            nfs-client     3m25s   Filesystem
persistentvolumeclaim/nfs-server-pvc-fast   Bound    pvc-adc24c97-ca67-4700-b3c5-2fc51c4cce01   10Gi       RWO            local-path     71m     Filesystem
persistentvolumeclaim/wp-pv-claim           Bound    wp-nfs-pv                                  4Gi        RWO            nfs-client     3m25s   Filesystem

🐤

서비스가 잘 작동하는것을 확인하였다.

(🐤 |DOIK-Lab:default) root@k8s-m:~/yaml/yaml# k exec nfs-server-rxvf7 -it -- /bin/bash
[root@nfs-server-rxvf7 /]# cd /exports/
[root@nfs-server-rxvf7 exports]# ll
total 32
drwxr-xr-x 5 systemd-bus-proxy root 4096 May 26 09:07 1
drwxr-xr-x 5                33   33 4096 May 26 09:07 2
-rw-r--r-- 1 root              root   16 May 26 07:59 index.html
[root@nfs-server-rxvf7 exports]# cd 1
[root@nfs-server-rxvf7 1]# ll
total 110608
-rw-rw---- 1 systemd-bus-proxy input       56 May 26 09:07 auto.cnf
-rw-rw---- 1 systemd-bus-proxy input 50331648 May 26 09:07 ib_logfile0
-rw-rw---- 1 systemd-bus-proxy input 50331648 May 26 09:07 ib_logfile1
-rw-rw---- 1 systemd-bus-proxy input 12582912 May 26 09:07 ibdata1
drwx------ 2 systemd-bus-proxy input     4096 May 26 09:07 mysql
drwx------ 2 systemd-bus-proxy input     4096 May 26 09:07 performance_schema
drwx------ 2 systemd-bus-proxy input     4096 May 26 09:07 wordpress
[root@nfs-server-rxvf7 1]# cd ..
[root@nfs-server-rxvf7 exports]# cd 2
[root@nfs-server-rxvf7 2]# ll
total 192
-rw-r--r--  1 33 33   418 Sep 25  2013 index.php
-rw-r--r--  1 33 33 19935 Jan  2  2017 license.txt
-rw-r--r--  1 33 33  7413 Dec 12  2016 readme.html
-rw-r--r--  1 33 33  5447 Sep 27  2016 wp-activate.php
drwxr-xr-x  9 33 33  4096 Oct 31  2017 wp-admin
-rw-r--r--  1 33 33   364 Dec 19  2015 wp-blog-header.php
-rw-r--r--  1 33 33  1627 Aug 29  2016 wp-comments-post.php
-rw-r--r--  1 33 33  2764 May 26 09:07 wp-config-sample.php
-rw-r--r--  1 33 33  3154 May 26 09:07 wp-config.php
drwxr-xr-x  4 33 33  4096 Oct 31  2017 wp-content
-rw-r--r--  1 33 33  3286 May 24  2015 wp-cron.php
drwxr-xr-x 18 33 33 12288 Oct 31  2017 wp-includes
-rw-r--r--  1 33 33  2422 Nov 21  2016 wp-links-opml.php
-rw-r--r--  1 33 33  3301 Oct 25  2016 wp-load.php
-rw-r--r--  1 33 33 34327 May 12  2017 wp-login.php
-rw-r--r--  1 33 33  8048 Jan 11  2017 wp-mail.php
-rw-r--r--  1 33 33 16200 Apr  6  2017 wp-settings.php
-rw-r--r--  1 33 33 29924 Jan 24  2017 wp-signup.php
-rw-r--r--  1 33 33  4513 Oct 14  2016 wp-trackback.php
-rw-r--r--  1 33 33  3065 Aug 31  2016 xmlrpc.php

🐤

목적이었던 NFS도 정상적으로 작동한다.

(🐤 |DOIK-Lab:default) root@k8s-m:~/yaml# k scale deployment wordpress --replicas=3

(🐤 |DOIK-Lab:default) root@k8s-m:~/yaml# k get pod
NAME                               READY   STATUS    RESTARTS   AGE
nfs-server-rxvf7                   1/1     Running   0          77m
wordpress-859f989bbb-8r5zh         1/1     Running   0          47s
wordpress-859f989bbb-msppd         1/1     Running   0          8m38s
wordpress-859f989bbb-xhbs9         1/1     Running   0          47s
wordpress-mysql-66fb7cfb68-z9vj5   1/1     Running   0          8m38s

(🐤 |DOIK-Lab:default) root@k8s-m:~/yaml# k get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                         STORAGECLASS   REASON   AGE
mysql-nfs-pv                               4Gi        RWO            Retain           Bound    default/mysql-pv-claim        nfs-client              9m6s
pvc-adc24c97-ca67-4700-b3c5-2fc51c4cce01   10Gi       RWO            Delete           Bound    default/nfs-server-pvc-fast   local-path              77m
wp-nfs-pv                                  4Gi        RWO            Retain           Bound    default/wp-pv-claim           nfs-client              9m6s
(🐤 |DOIK-Lab:default) root@k8s-m:~/yaml# k get pvc
NAME                  STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mysql-pv-claim        Bound    mysql-nfs-pv                               4Gi        RWO            nfs-client     9m2s
nfs-server-pvc-fast   Bound    pvc-adc24c97-ca67-4700-b3c5-2fc51c4cce01   10Gi       RWO            local-path     77m
wp-pv-claim           Bound    wp-nfs-pv                                  4Gi        RWO            nfs-client     9m2s

🐤

볼륨도 잘공유하여 프로비저닝 된것을 확인할수 있다. ㅜㅜ

Linux-stdin-stdout-stderr-2


stderr 는 버퍼를 사용하지 않지만 그것과 별개로 그대로 출력하기 때문에 grep 이 되지 않는것입니다.

https://www.facebook.com/groups/korelnxuser/permalink/2060620130779393/

소용환님께서 답변해주셔서 알 수 있었습니다.

그래서 테스트를 진행하였습니다.

stdbuf 명령어를 이용하여 buffer 를 제거하고 grep 해보았습니다. stdbuf -o0 는 stdout 를 unbuffered 로 출력하는 명력어 입니다.

# cat test.txt 
1
2
3
4
5
6
# stdbuf -o0 cat test.txt | grep 2
2

.

그런데 문득 버퍼사이즈가 0인것과 버퍼가 아주없는 unbuffered 는 차이가 있다는것을 알게되었습니다. 그래서 stderr 에 buffer 를 주었습니다.

# cat test.txt 1>&2 | stdbuf -eL grep 2
1
2
3
4
5
6

# stdbuf -eL cat test.txt 1>&2 | grep 2
1
2
3
4
5
6

.

grep 이 되지 않는것을 확인할수 있었습니다.

이 테스트를 결과로 stderr가 grep 되지 않는것은 buffer의 사용유무와 상관없이 그대로 출력하기 때문임을 알게되었습니다.

https://www.pixelbeat.org/programming/stdio_buffering/

이해를 돕기위해 pixelbeat.org 의 이미지를 첨부합니다.