K8s-one-line-Challenge

잔잔한 호수에 돌맹이는 내가던졌다.

K8s의 Service는 selector 에서 지정한 label로 pod에게 트래픽을 흘린다.

그런데 아이러니하게도 service 에서 연결된 pod를 한번에 조회할순 없다.

service 에서 selector 나 endpoint를 확인해서 labels 를 보고 확인해야 한다. 그 과정을 한번 보자.

my-service1 이라는 서비스에서 사용하는 pod를 조회할꺼다.

k get svc -o wide
NAME          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE     SELECTOR
kubernetes    ClusterIP   198.19.128.1     <none>        443/TCP        2d13h   <none>
my-service1   NodePort    198.19.231.233   <none>        80:30001/TCP   2d12h   app=my-nginx1
my-service2   NodePort    198.19.172.176   <none>        80:30002/TCP   2d12h   app=my-nginx2
my-service3   NodePort    198.19.200.20    <none>        80:30003/TCP   2d12h   app=my-nginx3

k get pods -l app=my-nginx1 --show-labels 
NAME                         READY   STATUS    RESTARTS   AGE   LABELS
my-nginx1-67f499d79c-g7vr7   1/1     Running   0          26h   app=my-nginx1,pod-template-hash=67f499d79c
my-nginx1-67f499d79c-j4f9k   1/1     Running   0          26h   app=my-nginx1,pod-template-hash=67f499d79c
my-nginx1-67f499d79c-mqxzs   1/1     Running   1          26h   app=my-nginx1,pod-template-hash=67f499d79c

kubectl. 에서 svc 를 get하고 -o wide 명령어를 쓰면 selector 가보인다. 거기서 get pod -l app=my-nginx1 이라 일일이 지정해줘야지만 확인할수 있다. 명령어 두줄치면 되긴한데 귀찮다. 이렇게 된이상 한줄치기는 물러설수 없다.

미끼를 물어주신 iamai 님께 감사를 드린다. - 재차 감사! - 예아!

kubectl get endpoints |grep my-service1 |awk '{print $2}'|tr "," "\n" |awk -F":" '{print $1}' |grep -f - <(kubectl get po -o wide)
my-nginx1-67f499d79c-g7vr7   1/1     Running    0          26h   198.18.0.74    nks-pool-1119-w-gzg   <none>           <none>
my-nginx1-67f499d79c-j4f9k   1/1     Running    0          26h   198.18.2.250   nks-pool-1119-w-gzh   <none>           <none>
my-nginx1-67f499d79c-mqxzs   1/1     Running    1          26h   198.18.1.208   nks-pool-1119-w-gzi   <none>           <none>

endpoint 에서 조회된 IP를 awk 로 떼어서 한줄씩으로 변환후 포트를 제거한다. 그리고 pod list 에서 IP가 grep 된 줄만 출력한다.

[root@linuxer-bastion ~]# kubectl get endpoints
NAME          ENDPOINTS                                         AGE
kubernetes    10.0.12.10:6443,10.0.12.11:6443,10.0.12.16:6443   2d13h
my-service1   198.18.0.74:80,198.18.1.208:80,198.18.2.250:80    2d13h
my-service2   198.18.0.173:80,198.18.1.155:80,198.18.2.120:80   2d13h
my-service3   198.18.0.6:80,198.18.1.139:80,198.18.2.70:80      2d13h
[root@linuxer-bastion ~]# kubectl get endpoints |grep my-service1 
my-service1   198.18.0.74:80,198.18.1.208:80,198.18.2.250:80    2d13h
[root@linuxer-bastion ~]# kubectl get endpoints |grep my-service1 |awk '{print $2}'
198.18.0.74:80,198.18.1.208:80,198.18.2.250:80
[root@linuxer-bastion ~]# kubectl get endpoints |grep my-service1 |awk '{print $2}'|tr "," "\n" 
198.18.0.74:80
198.18.1.208:80
198.18.2.250:80
[root@linuxer-bastion ~]# kubectl get endpoints |grep my-service1 |awk '{print $2}'|tr "," "\n" |awk -F":" '{print $1}' 
198.18.0.74
198.18.1.208
198.18.2.250
[root@linuxer-bastion ~]# kubectl get endpoints |grep my-service1 |awk '{print $2}'|tr "," "\n" |awk -F":" '{print $1}' |grep -f - <(kubectl get po -o wide)
my-nginx1-67f499d79c-g7vr7   1/1     Running    0          26h   198.18.0.74    nks-pool-1119-w-gzg   <none>           <none>
my-nginx1-67f499d79c-j4f9k   1/1     Running    0          26h   198.18.2.250   nks-pool-1119-w-gzh   <none>           <none>
my-nginx1-67f499d79c-mqxzs   1/1     Running    1          26h   198.18.1.208   nks-pool-1119-w-gzi   <none>           <none>
[root@linuxer-bastion ~]# kubectl get po -o wide
NAME                         READY   STATUS     RESTARTS   AGE   IP             NODE                  NOMINATED NODE   READINESS GATES
busybox                      0/1     Init:0/2   0          26h   198.18.2.60    nks-pool-1119-w-gzh   <none>           <none>
my-nginx1-67f499d79c-g7vr7   1/1     Running    0          26h   198.18.0.74    nks-pool-1119-w-gzg   <none>           <none>
my-nginx1-67f499d79c-j4f9k   1/1     Running    0          26h   198.18.2.250   nks-pool-1119-w-gzh   <none>           <none>
my-nginx1-67f499d79c-mqxzs   1/1     Running    1          26h   198.18.1.208   nks-pool-1119-w-gzi   <none>           <none>
my-nginx2-659945d9d8-2sggt   1/1     Running    1          26h   198.18.1.155   nks-pool-1119-w-gzi   <none>           <none>
my-nginx2-659945d9d8-cjkft   1/1     Running    0          26h   198.18.2.120   nks-pool-1119-w-gzh   <none>           <none>
my-nginx2-659945d9d8-szw59   1/1     Running    0          26h   198.18.0.173   nks-pool-1119-w-gzg   <none>           <none>
my-nginx3-694994cd8c-l4m6k   1/1     Running    1          26h   198.18.1.139   nks-pool-1119-w-gzi   <none>           <none>
my-nginx3-694994cd8c-lbqsd   1/1     Running    0          26h   198.18.2.70    nks-pool-1119-w-gzh   <none>           <none>
my-nginx3-694994cd8c-xjzkc   1/1     Running    0          26h   198.18.0.6     nks-pool-1119-w-gzg   <none>           <none>
nginx                        1/1     Running    0          26h   198.18.0.80    nks-pool-1119-w-gzg   <none>           <none>

이해를 돕기위해 결과를 한줄씩 쳐서 출력했다.

iamai 님의 shell에 대한 이해도를 볼수있었다.

나는 grep를 쓰지않고 출력하고 싶었다. 여러 엔지니어들을 보면 jsonpath로 예쁘게 깍는것이 부러웠다. 방법은 다양했다 json | jq 부터 jsonpath custom-columns 까지 방법이 많은데 나도 한번 써볼까 싶었다.

k get pod -l $(k get svc my-service1 -o=jsonpath='{..selector}' | sed 's/map//' | sed 's/:/=/' | tr -s '[[:space:]]' ' ') --show-labels
NAME                         READY   STATUS    RESTARTS   AGE   LABELS
my-nginx1-67f499d79c-g7vr7   1/1     Running   0          26h   app=my-nginx1,pod-template-hash=67f499d79c
my-nginx1-67f499d79c-j4f9k   1/1     Running   0          26h   app=my-nginx1,pod-template-hash=67f499d79c
my-nginx1-67f499d79c-mqxzs   1/1     Running   1          26h   app=my-nginx1,pod-template-hash=67f499d79c

시작부터 iamai 님과의 다른접근을 볼수있다. 나는 label로 접근했고, iamai 님은 IP로 접근했다.

selector 는 label을 기반으로 pod와 매핑되기때문에 IP가 우선이 되서는 안된다 생각했다. IP는 고정된 값이 아니므로. 그래서 label 을 사용하기로 생각했다.

[root@linuxer-bastion ~]# k get svc my-service1 -o=jsonpath='{..selector}' 
map[app:my-nginx1]
[root@linuxer-bastion ~]# k get svc my-service1 -o=jsonpath='{..selector}' | sed 's/map//' 
[app:my-nginx1]
[root@linuxer-bastion ~]# k get svc my-service1 -o=jsonpath='{..selector}' | sed 's/map//' | sed 's/:/=/' 
[app=my-nginx1]
[root@linuxer-bastion ~]# k get svc my-service1 -o=jsonpath='{..selector}' | sed 's/map//' | sed 's/:/=/' | tr -s '[[:space:]]' ' '
 app=my-nginx1

먼저 jsonpath를 이용하여 service의 selector를 찾는다 여기서 sed 명령어로 map[app:my-nginx1] 이라는 문자열을 두번 파이프라인하여 [app=my-nginx1]로 변환된다. json으로 출력한 문자열은 = -> : 으로 치환되어 표기된다. 그래서 변경해줘야 했다. 괄호를 벗겼다. 괄호를 벗은 값은 내가 처음부터 원했던 service - selector - label 이다. 이제 이값을 이용해서 pod 를 리스팅 하고 label를 보면 완성이다.

k get pod -l $(k get svc my-service1 -o=jsonpath='{..selector}' /| sed 's/map//' | sed 's/:/=/' | tr -s '[[:space:]]' ' ') --show-labels
NAME                         READY   STATUS    RESTARTS   AGE   LABELS
my-nginx1-67f499d79c-g7vr7   1/1     Running   0          26h   app=my-nginx1,pod-template-hash=67f499d79c
my-nginx1-67f499d79c-j4f9k   1/1     Running   0          26h   app=my-nginx1,pod-template-hash=67f499d79c
my-nginx1-67f499d79c-mqxzs   1/1     Running   1          26h   app=my-nginx1,pod-template-hash=67f499d79c

내가 작성한 스크립트는 폰트크기 15다

k get ep -o custom-columns=IP:.subsets[].addresses[].ip
IP
10.0.12.10
198.18.0.74
198.18.0.173
198.18.0.6

성주님께서 주신 IP 추출 팁

오랜만에 머리를 굴렸더니 재미있었다.

오늘도 같이 머리를 싸매서 고민을 해주신 봄님, iamai 님, 성주님께 감사를 드린다.

더좋은 아이디어나 생각이 있다면 얼른 결과를 공유해주시길 바란다!

즐거운 새벽되시라!

하고 누우려는데 성주님께서 주신 IP list 로 하나더 만들고 싶었다.

k get ep my-service1 -o custom-columns=IP:.subsets[].addresses[*].ip | tr "," "\n" | grep -v IP | grep -f - <(kubectl get po -o wide --show-labels) 
my-nginx1-67f499d79c-g7vr7   1/1     Running    0          27h   198.18.0.74    nks-pool-1119-w-gzg   <none>           <none>            app=my-nginx1,pod-template-hash=67f499d79c
my-nginx1-67f499d79c-j4f9k   1/1     Running    0          27h   198.18.2.250   nks-pool-1119-w-gzh   <none>           <none>            app=my-nginx1,pod-template-hash=67f499d79c
my-nginx1-67f499d79c-mqxzs   1/1     Running    1          27h   198.18.1.208   nks-pool-1119-w-gzi   <none>           <none>            app=my-nginx1,pod-template-hash=67f499d79c

성주님+iamai님의 조언을 합쳤다 중간에 grep -v IP 는 내 생각이다.

시원하게 끝내고 잔다!

정말로 좋은새벽되시라.

Ping-MTU-test

MTU 9000 이상을 점보프레임이라 부른다.

점포프레임이 정상적으로 전송되는지 확인하는 방법이다.

ping -M do -s 1472 google.com
PING google.com (172.217.175.110) 1472(1500) bytes of data.
76 bytes from nrt20s21-in-f14.1e100.net (172.217.175.110): icmp_seq=1 ttl=114 (truncated)
76 bytes from nrt20s21-in-f14.1e100.net (172.217.175.110): icmp_seq=2 ttl=114 (truncated)
76 bytes from nrt20s21-in-f14.1e100.net (172.217.175.110): icmp_seq=3 ttl=114 (truncated)

1500에 맞춰서 google로 보내면 정상적으로 간다.

ping -M do -s 1473 google.com
PING google.com (172.217.175.110) 1473(1501) bytes of data.
^C
--- google.com ping statistics ---
45 packets transmitted, 0 received, 100% packet loss, time 43999ms

1501 은 가지 않는다.

대부분의 클라우드 내부의 이더넷은 점보프레임이 설정되어 있으며 9000이상이다.

ifconfig | grep -i MTU | grep eth
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 8950

외부망으론 1500까지만 전송됨을 확인하였으니 내부망에서 MTU를 확인한다.

ping -M do -s 8922 10.0.12.13
PING 10.0.12.13 (10.0.12.13) 8922(8950) bytes of data.
8930 bytes from 10.0.12.13: icmp_seq=1 ttl=64 time=0.491 ms
8930 bytes from 10.0.12.13: icmp_seq=2 ttl=64 time=0.431 ms
8930 bytes from 10.0.12.13: icmp_seq=3 ttl=64 time=0.483 ms

잘된다.

인터페이스에 지정된 MTU는 8950이다.
ping에선 IP Header(20 Bytes) + ICMP Header(8 Bytes) 28 Bytes 를 뺀 숫자가 ICMP Data 크기이다. 그래서 Ping 로 MTU 테스트할땐 실제 MTU-28을 하여 테스트하면된다.

linux-sed

일반적으로 sed를 쓸때 나는

sed s/원문/치환/ 파일

이런식으로 사용했다. 그런데 만약에 변경할것이 /var/log 에서 /var/log2로 변경한다면

sed s/₩/var₩/log/₩/var₩/log2/ 파일

이런식으로 sed의 구분자를 회피하기위해 ₩/ 과같은 역슬러쉬를 사용해야 했다. 그런데 오늘 혁신을 맛봤다.

sed "s|/var/log|/var/log2|" 파일

/ 대신 |를쓰면 ₩/를 일일이 쓸필요가 없다..

하..지금까지의 내 하드코딩 돌려줘ㅠㅠ

후에 게시물을 공유하고

"In a context address, any character other than a backslash (``\'') or newline character may be used to delimit the regular expression."

sed man 에 백슬러쉬랑 엔터 빼고 다된다는 이야기를 들었다..ㅠㅠㅠㅠㅠㅠ

진작알았다면 좋았을껄..ㅠㅠㅠㅠㅠㅠㅠㅠ

kubernetes-CentOS7-install

작년 10월 작성했던 install script가 달라졌다.

<#!/bin/sh
setenforce 0
sed -i --follow-symlinks 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/sysconfig/selinux
swapoff -a
modprobe br_netfilter
echo '1' > /proc/sys/net/bridge/bridge-nf-call-iptables
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y docker-ce
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
yum install -y kubelet kubeadm kubectl
sed -i "s|ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock|ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --exec-opt native.cgroupdriver=systemd|" /usr/lib/systemd/system/docker.service
echo "net.bridge.bridge-nf-call-ip6tables = 1" >> /etc/sysctl.conf
echo "net.bridge.bridge-nf-call-iptables = 1" >> /etc/sysctl.conf
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p
systemctl enable docker
systemctl enable kubelet

.

큰 틀의 변화라면 작년의 클러스터 설치에선 kubeadm.conf 에서 docker와 kube의 cgroup을 변경해주는 방식이 었다면 근래에는 Docker의 Cgroup를 변경해야한다.

error: failed to run Kubelet: failed to create kubelet:
misconfiguration: kubelet cgroup driver: "systemd" is different from docker cgroup driver: "cgroupfs"

.

그렇지 않으면 위와같은 에러가 발생한다. 이에러를 피하기위해 sed 로 Docker의 systemctl service file에서 docker exec 구문에 --exec-opt native.cgroupdriver=systemd native driver의 실행을 수정해 주면된다.

변경된지 확인하는방법은

docker info | grep systemd
Cgroup Driver: systemd

.

명령어로 cgroup 를 확인할수 있다.

여기까지 확인되었으면, 작업한 Server의 스냅샷을 생성후, 스냅샷을 기반으로 워커노드3대를 생성한다.

위에작성한 스크립트는 init script 에서도 프로비저닝시에 사용할수있도록 만들었으니 그냥 사용해도 된다.

스냅샷이 잘 생성되면 kubectl init 를 하자.

kubeadm init --pod-network-cidr=10.254.0.0/16

.

init 가 끝나면 config 를 복사해준다

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
export KUBECONFIG=/etc/kubernetes/admin.conf

.

명령어로 복사를한다. 설치과정에서 프린팅된다 admin.conf 까지 같이 export 한다.

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 10.0.10.9:6443 --token 6ghues.yxwqnp87920d4arm \
	--discovery-token-ca-cert-hash sha256:49c750f19ef34b5f3ab73ceb91a2ce540a65418b693c24a1ee5acaeee2e80972 

.

다음과 같은 내용이 프린팅되는데 join 부분만 쓰면된다.

woker node에서 join한다

[preflight] Running pre-flight checks
	[WARNING Hostname]: hostname "k8s-worker-003" could not be reached
	[WARNING Hostname]: hostname "k8s-worker-003": lookup k8s-worker-003 on 169.254.169.53:53: no such host
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

.

다음 메시지가 나오면 정상적으로 클러스터가 셋팅된 것이다.

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

.

bash 에서 shell 자동완성 설정까지 넣어주면 완성이다.

TAB을 이용한 자유를 누려보자!

Basic-Docker

오랜만의 블로깅이다.

오랜만에 글을 쓰는것은 Docker 다.

간단히 도커 설치부터 이야기를 해보겠다. 나는 Redhat 계열의 리눅스를 좋아하므로 Centos7 로 진행하려 한다.

yum 으로 도커를 설치할건데, 몇가지를 선택해야 한다.

도커를 제공하는 레포는 여러가지가 있는데, 나는 Centos 의 Extra repo를 좋아한다. 다른레포를 사용하지 않고 그냥 바로 설치 할수 있기 때문이다.

https://docs.docker.com/engine/install/centos/

다음 URL을 참고하자.

물론 최신버전과는 거리가 좀 많다. Extra repo 는 1.13버전을 제공하며, 현재 Docker-ce repo에서 제공하는 버전은 1.20다.

Docker를 사용하기 위해선 6개의 패키지가 필요하다.

[root@linuxer ~ ]# yum install -y docker

(1/6): container-selinux-2.119.2-1.911c772.el7_8.noarch.rpm                     
|  40 kB  00:00:00     
(2/6): container-storage-setup-0.11.0-2.git5eaf76c.el7.noarch.rpm               
|  35 kB  00:00:00     
(3/6): containers-common-0.1.40-11.el7_8.x86_64.rpm                             
|  43 kB  00:00:00     
(4/6): docker-client-1.13.1-162.git64e9980.el7.centos.x86_64.rpm                
| 3.9 MB  00:00:00     
(5/6): docker-common-1.13.1-162.git64e9980.el7.centos.x86_64.rpm                
|  99 kB  00:00:00     
(6/6): docker-1.13.1-162.git64e9980.el7.centos.x86_64.rpm                       
|  18 MB  00:00:00 

docker 만 설치해도 의존성으로 도커를 사용하기위한 패키지를 설치해준다.
아이러니 하게 yum remove docker 로 삭제하면 docker는 그냥혼자 지워진다. 설치할땐 패거리로 오고 갈땐 혼자간다. 구버전이라 패키지 네이밍도 좀 올드 하다.

docker-ce repo 에서 지원하는 패키지를 확인해보면 이렇다.

[root@linuxer ~ ]# yum install -y docker-ce

(1/6): container-selinux-2.119.2-1.911c772.el7_8.noarch.rpm                      
|  40 kB  00:00:00     
(2/6): docker-ce-20.10.7-3.el7.x86_64.rpm                                       
|  27 MB  00:00:00     
(3/6): containerd.io-1.4.6-3.1.el7.x86_64.rpm                                  
|  34 MB  00:00:00     
(4/6): docker-ce-rootless-extras-20.10.7-3.el7.x86_64.rpm                      
| 9.2 MB  00:00:00     
(5/6): docker-scan-plugin-0.8.0-3.el7.x86_64.rpm                                
| 4.2 MB  00:00:00     
(6/6): docker-ce-cli-20.10.7-3.el7.x86_64.rpm                                   
|  33 MB  00:00:02     

패키지의 역할을 각각 설명하자면, docker 의 데몬을 실행하기 위한 패키지 그리고 우리가 흔히 쓰는 docker 명령어를 포함한 docker-cli, 그리고 containerd는 컨테이너를 실행하고 노드에서 이미지를 관리하는데 필요한 기능을 제공하는 OCI다. OCI는 Open Container initiative 라고 하는데 업계 표준이라 생각하면 간단하다.

패키지의 이름과 역할 분류가 조금씩 변한거다.

그럼 이제 좀 원론적인 이야기를 한번 해야겠다.

도커는 격리된 프로세스다. 도커를 실행중인 리눅스 시스템에서 아주 쉽게 알수있다.

docker run -d -p 8080:80 ngnix 명령어로 도커를 실행했다.

그리고 도커를 실행중인 호스트에서 ps 명령어를 쳤다.

[root@linuxer ~ ]# ps afxuwww

/usr/bin/containerd-shim-runc-v2 -namespace moby -id 
\_ nginx: master process nginx -g daemon off;
\_ nginx: worker process
\_ nginx: worker process

ps afxuwww 명령어에서 보기쉽게 트리만 떼온 상태다. containerd-shim-runc 데몬이 nginx를 실행중인것을 알 수 있다. 이것만으로도 컨테이너는 프로세스다. 라는것을 알수있다.

이렇기에 컨테이너는 불 안정함을 띄고 있고, 취약한 부분이 있다.

이제 도커 파일을 이야기해보려한다.

근래엔 도커파일의 사용법을 다들 잘아는 터라 Docs 로 대체한다.

https://docs.docker.com/engine/reference/builder/

근래에 내가 만든 도커파일은 이렇다.

FROM centos:7
LABEL maintainer "linuxer<linuxer@linuxer.name>"
LABEL "purpose"="practice"
ENV PATH /opt/remi/php80/root/usr/sbin:$PATH
RUN yum -y update
RUN yum -y install epel-release
RUN yum -y install https://rpms.remirepo.net/enterprise/remi-release-7.rpm
RUN yum -y install nginx php80-php-fpm
RUN mkdir /var/run/php
RUN mkdir /root/bin
ADD www.conf /etc/opt/remi/php80/php-fpm.d/
ADD php.ini /etc/opt/remi/php80/
ADD nginx.conf /etc/nginx/
ADD index.php /usr/share/nginx/html/
ADD start.sh /root/bin/
RUN chmod 755 /root/bin/start.sh
WORKDIR /usr/share/nginx/html/
EXPOSE 80
CMD ["/bin/bash", "-c", "/root/bin/start.sh"]

ngnix와 php80 버전을 합친 Dockerfile이다.

ADD한 파일들은 직접 파라미터를 수정한터라 스킵하고 start.sh에 대해서 이야기하려한다. 일반적으로 컨테이너는 포그라운드에서 동작한다. 백그라운드에서 동착한다면 작업을 마친 프로세스는 스스로 종료된다. 여기에서 문제가 발생한다. 도커는 CMD로 시작되는 프로세스는 아무리 많이 입력 되어도 마지막 줄만 실행된다. 그렇기에 두개의 프로세스를 한번에 실행할수 없었다. 이게바로..포그라운드의 문제점이었다.

이문제점을 회피하기위해서 수십가지의 테스트를 하였다.

CMD ["php-fpm", "-f", "&", "nginx", "-g", "daemon", "off"]

이런짓까지.. 하지만 실패했고 결국 스크립트를 작성하여 실행하도록 하고 fg %1 과같은 명령어를 썼다.

[root@linuxer ~ ]# cat start.sh

#!/bin/bash
set -m
/opt/remi/php80/root/usr/sbin/php-fpm --nodaemonize \
--fpm-config /etc/opt/remi/php80/php-fpm.conf & \
/usr/sbin/nginx
fg %1

다른사람들에게 팁을 얻으려고 했으나, 안티 패턴인지 딱히 다들 말이없었다. 컨테이너이므로 쪼개 라는 대답을 얻었다. 나도 알고있는 부분인지라 사실 쪼갤까 했지만 일단 스크립트를 써서 완성을 했다.

이렇게 완성한 컨테이너 를 실행해 보면

[root@linuxer ~ ]# ps afxuwww
/usr/bin/containerd-shim-runc-v2 -namespace moby -id 4
      \_ php-fpm: master process (/etc/opt/remi/php80/php-fpm.conf)
      |   \_ php-fpm: pool www
      |   \_ php-fpm: pool www
      |   \_ php-fpm: pool www
      |   \_ php-fpm: pool www
      |   \_ php-fpm: pool www
      \_ nginx: master process /usr/sbin/nginx
          \_ nginx: worker process
          \_ nginx: worker process

두개의 부모프로세스를 가진 컨테이너를 확인할수 있다.

사실 이건 테스트 하느라 만든 방법이고 실제로는 nginx 를 lb로 이용하여 php-fpm 으로 라우팅 하는 구조를 만들꺼다. 그전에 손풀기로 만들어본 이미지 이나, 나름 재미있어서 포스팅까지 진행했다.

좋은밤되시라!