레이블이 dockerfile인 게시물을 표시합니다. 모든 게시물 표시
레이블이 dockerfile인 게시물을 표시합니다. 모든 게시물 표시

docker alpine not found error

# app(바이너리파일)을 추가한 apline 도커 이미지에서 다음과 같은 에러가 발생했다.
/ysoftman # ./app
/bin/sh: ./app: not found

# 다음과 같이 lib6-compat(glibc 호환 라이브러리 패키지)를 설치하면 된다.
RUN apk add libc6-compat

# 추가로 libstdc++, libgcc 등 을 찾을 수 없는 경우 설치하자.
RUN apk add libstdc++ libgcc

k8s 디폴트 서비스로 커스텀 에러 페이지 응답하기

# 다음과 같은 흐름에서
ysoftman.test.com --> 10.10.10.100 (VIP) --> 10.10.10.11,10.10.10.12 (ingress node)

# 도메인으로 접속을 하면 서비스나 nginx controller 의 custom errorpage 로 응답하는데,
curl ysoftman.test.com

# ip 로 요청하면 nginx 기본 404 page(html)을 응답준다.(nginx 정보가 표시되어 보안 수정 사항!)
curl 10.10.10.100
<html>
  <head><title>404 Not Found</title></head>
  <body>
    <center><h1>404 Not Found</h1></center>
    <hr><center>nginx</center>
  </body>
</html>

# nginx pod ssh 접속해 설정을 보면
vi /etc/nginx/nginx.conf 
# backend 설정이 되지 않거나 endpoints 가 없는 경우 기본 404 페이지를 리턴한다.
... 생략 ...
        # backend for when default-backend-service is not configured or it does not have endpoints
        server {
                listen 8181 default_server reuseport backlog=16777216;
                set $proxy_upstream_name "internal";
                access_log off;
                location / {
                        return 404;
                }
        }
... 생략 ...

# 전체 네임스페이스를 찾아보니 디폴트 백엔드가 설정되지 않는 ingress 설정들이 있었다.
kubectl describe ing --all-namespaces | rg 'not found' -B 2
Namespace:        aaaa
Address:          10.10.10.11,10.10.10.12
Default backend:  default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
... 생략 ...

# 각 네임스페이스 인그레스에 디폴트 백엔드 설정을 하긴 힘들고
# 새 서비스 추가할때마다 backend servcie 설정을 신경써야 하기 때문에
# nginx controller daemoneset (없다면 deployment)의 yaml에 args 부분에
# 다음과 같이 default-backend-service 로 nginx-controller 자체에 디폴트 백엔드를 명시할 수 있다.
   spec:
      containers:
      - args:
        - /nginx-ingress-controller
        - --enable-ssl-chain-completion=false
        - --configmap=$(POD_NAMESPACE)/ingress-nginx
        - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
        - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
        - --annotations-prefix=nginx.ingress.kubernetes.io
        - --report-node-internal-ip-address
        - --default-backend-service=ysoftman-namespace/ysoftman-service

# 기본적으로 default-backend-service 는 / 404 와 /healthz 200 만 노출한다.
# default-backend-service 으로 보낼 http 에러 코드를 추가 할 수있다. 
# ingress-nginx-controller -> configmap 에 다음 키와 값을 추가한다
... 생략 ...
data:
  custom-http-errors: 400,401,403,404,405,500,503,505

# 이제 default-backend-service 에서는 X-Code 등의 특정 헤더로 전달 받을 수 있다.

#####

# default-backend-service 는 다음 예제를 사용할수도 있지만
# caddy 도커 이미지로 default-backend-service 를 만들었다.
COPY Caddyfile /etc/caddy/Caddyfile
# index.html 를 에러 페이지로 덮어쓰기
COPY error404.html /usr/share/caddy/error404.html
COPY error404.html /usr/share/caddy/index.html
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]

# Caddyfile 은 다음과 같다.
:80
root * /usr/share/caddy
file_server
handle_errors {

#    @404 {
#        expression {http.error.status_code} == 404
#    }

# rewrite @404 /error404.html
    @fromNginxController40x {
        header X-Code 40*
    }
    @fromNginxController50x {
        header X-Code 50*
    }
    @4xx {
        expression "{http.error.status_code} >= 400 && {http.error.status_code} < 500"
    }
    @5xx {
        expression "{http.error.status_code} >= 500 & {http.error.status_code} < 600"
    }
    rewrite @fromNginxController40x /error404.html
    rewrite @fromNginxController50x /error404.html
    rewrite @4xx /error404.html
    rewrite @5xx /error404.html
    file_server
}
log {
    level INFO
    output file /tmp/caddylog.log {
        roll_size 100MiB
        roll_keep 5
        roll_keep_for 48h
    }
}

# caddy 를 로컬 컨테이너로 올려 테스트하면 설정한 에러 페이지가 잘 보인다.
curl http://localhost
curl http://127.0.0.1
curl http://127.0.0.1/aaaaa

# nginx controller --default-backend-service 에 설정하면 에러가 발생한다.
connect() failed (111: Connection refused) while connecting to upstream, 
 client: 10.10.10.11, server: _, request: "GET /aaaaa HTTP/1.1", upstream: "http://10.10.10.12:443/aaaaa"

# 위 에러 로그를 보면 http 인데 443 포트를 사용하고 있다.
# caddy default-backend-service 에 다음과 같이 443 포트가 설정되어 있었다.
apiVersion: v1
kind: Service
metadata:
  namespace: default
  name: default-backend-service
spec:
  ports:
    - name: caddy-https
      port: 443
      targetPort: 443
    - name: caddy-http
      port: 80
      targetPort: 80
  selector:
    app: default-backend-service-deployment

# http 인데도 443 포트를 우선으로 시도 하는 것으로 보인다.
# 443 포트 설정을 제거하고 80 포트만 사용하니 잘된다.
spec:
  ports:
    - name: caddy-http
      port: 80
      targetPort: 80

docker multistage

# dockerfile 에서 
# RUN, ADD, COPY 명령마다 layer 가 생성된다. 기존의 이미지에서 이 명령들 중 수정되면 수정되는 곳 부터 새로운 layer 가 생성된다.
# CMD, LABEL, ENV, EXPOSE 임시 layer 로 생성되지만 실제 도커이미지에는 저장되지 않는다.

# layer 개수와 해시값(sha256)은 다음 명령으로 확인할 수 있다.
docker image inspect nginx:latest  | jq '.[0].RootFS'

# 최적화된 도커 이미지 생성하기
# 베이스가 될 이미지 사이즈가 작은것을 선택한다.
# alpine linux 는 유닉스 기본 명령어가 포함된 busybox(https://www.busybox.net/) 와 musl(https://musl.libc.org/) C 라이브러리만 포함하고 있어 사이즈가 아주 작다.
# 다만 python 을 사용한다면 apline 기반에서는 패키지 철치, 빌드 시간, 기타 취약점등 단점이 많기 때문에 debian(https://www.debian.org/releases/) 10(buster)나 11(bullseye) 을 사용하는것이 좋다.

# RUN 하나로 layer 개수를 줄이자.
# 다음의 경우 RUN 마다 layer 가 생성되지만 
RUN apt-get update
RUN apt-get install -y gcc
RUN apt-get install -y git

# 다음과 같이 하나로 합치면 layer 도 1개 된다.
RUN apt-get update && apt-get install -y gcc && apt-get install -y git

# 자주 내용이 바뀔 수 있는 COPY 명령은 변경이 있을 때마다 새로 layer 생성을 위해 빌드 시간이 추가되기 때문에 변경이 없는 명령 이후에 위치하여 불필요한 빌드 시간을 단축하는것이 좋다.

# 멀티 스테이지 사용하기
# Docker 17.05 부터 Dockerfile 하나에 여러개의 빌드 스테이지를 둘 수 있다.
# FROM 만큼 스테이지를 생성(이미지 빌드)하고
# 이전 스테이지의 결과를 다음 스테이즈로 복사해서 사용할 수 있다.
# 스테이지1) 소스 빌드 바이너리 --> 스테이지2) 바이너리만 포함된 이미지
# 와 같은 작업을 dockerfile 하나로 작성할 수 있어 편하다.

# Dockerfile
# buildstage 이름의 스테이지로 지정
FROM ubuntu:latest AS buildstage

WORKDIR /root
RUN echo "test" > output.txt

# 만약 또다른 스테이지(buildstage123)가 있으면 이 buildstage, buildstage123 스테이지 둘다 동시에 진행되고,   
# buildstage 에서 다음과 같이 buildstage123 의 결과물을 복사명령이 있다면 buildstage123 가 완료 후 COPY 가 수행된다.
# COPY --from=buildstage123 /xxx.txt .

##########

# 마지막 스테이지
FROM archlinux/base:latest

# buildstage 의 결과물을 현재 스테이지의 . 위치로 복사
COPY --from=buildstage /root/output.txt .
CMD ["/bin/bash"]

# multistage 를 빌드하면 최종 스테이지 말고는 
# 모두 <none> dangling 이미지가 생성되니 지워준다.
# docker image prune -f

# 참고로 dive(https://github.com/wagoodman/dive) 툴을 이용하면 docker image layer 별 사이즈 및 세부내용을 볼수 있어 이미지 분석에 도움이 된다.


Dockerfile ENV 사용하기

# Dockerfile ENV 주의사항
FROM centos:7
RUN echo $PATH
RUN mkdir -p /ysoftman && cd /ysoftman && echo 'echo ysoftman' > ysoftman.sh
RUN echo "PATH=${PATH}:/ysoftman" >> ${HOME}/.bashrc
RUN cat ${HOME}/.bashrc

# source 현재 쉘에 PATH 환경변수를 새로 반영해도 현재 RUN에서만 유효하다.
# RUN source ${HOME}/.bashrc && echo $PATH
# 위 RUN source 내용은 현재 RUN 에 반영되지 않는다.
RUN echo $PATH

# dockerfile 처리중 환경변수 반영은 ENV 를 사용해야 한다.
ENV PATH=${PATH}:/ysoftman
RUN ls -ahl /ysoftman/
RUN echo $PATH && cd ${HOME} && bash ysoftman.sh


#####


# 기본 root 계정으로 실행한다.
RUN whoami

# root 의 HOME 디렉토리인 /root 가 찍한다.
RUN echo ${HOME}

# ENV 에선 HOME 변수가 설정된것이 없다.
ENV mypath="${HOME}:aa"

# :aa 로 찍힌다.
RUN echo ${mypath}

# HOME 환경변수를 별도로 설정해야 한다.
ENV HOME="/root"
ENV mypath="${HOME}:aa"

# /root:aa 로 찍힌다.
RUN echo ${mypath}


#####


# 다음과 같이 arg 를 입력 받아
# docker --build-arg aaa=lemon -f Dockerfile .

# Dockerfile 에서 사용할때
ARG aaa
# CMD 는 ARG 를 바로 사용할 수 없어 ENV 를 통해야 한다.
ENV aaa=${aaa}
CMD echo ${aaa}

docker 설치 및 사용

docker(도커)는 리눅스의 컨테이너 기술을 이용한 가상화 플랫폼이다.
기존 가상화 시스템인 VMware, VirtualBox 등과 달리 Guest OS 를 설치하지 않고 Host OS 의 자원을 공유해서 사용할 수 있어 빠른 장점이 있다.
(기존 가상화 시스템은 게스트 OS 와 호스트 OS 를 사이에 Hypervisor 가 존재하고 이과정으로 인해 전반적인 속도 저하가 발생한다.)
도커는 이미지(image)라는 일종의 파일 묶음을 컨테이너(container)라는 일종의 실행환경 인스턴스로 만들어 사용하게 된다.
도커는 리눅스에서 동작하지만 Mac 이나 Windows 에서 Docker ToolBox 를 이용해서 사용할 수 있다.

# 윈도우에서 docker 설치하기(legacy)
# DockerToolBox 다운로드 https://www.docker.com/products/docker-toolbox
# 아래 그림 처럼 docker compose, virtualbox, kitematic 를 모두 설치하자.

# Docker QuickStart Terminal 실행하면 도커의 마스코트 고래가 짜잔~ㅎ
# 참고로 초기 실행시 환경 설정으로 느리고 다음 실행 부터는 빠르게 프롬프트가 표시된다.

# dockertoolbox 는 legacy 로 대신 docker ce(community edition) 을 설치할 수 도 있다.
# 윈도우 도커 다운로드 https://docs.docker.com/docker-for-windows/install/

#####

# 맥에서 docker 설치
# Docker.dmg 다운로드 https://docs.docker.com/docker-for-mac/install/
# docker.app 실행 하면 도커 데몬이 실행되고 상단에 다음과 같은 고래 아이콘이 보인다.

# 2017년 현재 mac 에서 docker 사용시 몇자기 제한이 있다.
# docker0 브리지가 없고 컨테이너 ip 설정이 안된다.
https://docs.docker.com/docker-for-mac/networking/#known-limitations-use-cases-and-workarounds
# 설정 파일을 이용한다면
vi /etc/docker/daemon.json
{
  "data-root": "/home/ysoftman/docker",
  "insecure-registries" : ["myregistrydomain.com:5000", "ysoftman.com:5000"]
}
sudo service docker restart

#####

# centos 에서 docker 설치
# 기존 설치된 docker 모두 삭제
sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-selinux \
                  docker-engine-selinux \
                  docker-engine
# 도커 설치 파일 다운로드
wget https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-17.12.1.ce-1.el7.centos.x86_64.rpm
# 설치(rpm 파일은 yum 으로도 설치할 수 있다.)
sudo yum install ./docker-ce-17.12.1.ce-1.el7.centos.x86_64.rpm

#####
# linux 에서 sudo 없이 docker 명령 사용하기
# 방법1 - docker 그룹 추가 (/etc/group)
sudo groupadd docker
# /etc/group 에 docker 그룹에 현재 사용자 추가
sudo gpasswd -a $USER docker
# 소켓파일 docker 그룹으로 변경
sudo chgrp docker /var/run/docker.sock
# 터미널 로그아웃/로그인
# 방법2 - socket 파일 권한 변경
# docker.sock file acl 보기
getfacl /var/run/docker.sock
# docker.sock docker 그룹 권한 설정
sudo setfacl -m g:docker:rw /var/run/docker.sock
# docker.sock docker 그룹 권한 삭제
sudo setfacl -x g:docker /var/run/docker.sock
# 방법3 - socket 파일 현재 사용자:그룹으로 권한 변경
sudo chown -R ysoftman:ysoftman /var/run/docker.sock

#####
# docker command
# 도커 이미지 저장 위치(docker root dir) 저장소(registry) 등 정보 확인
docker info
# 현재 가지고 있는 이미지 목록 보기
docker images
# 도커 hub 에 올라온 nginx 이미지 검색하기
docker search nginx
# description 에 Official 이라고 명시된 공식 이미지를 다운 받자.
# nginx:123 와 같이 태그를 명시하지 않으면 가장 최근 버전을 다운 받는다.
docker pull nginx
# nginx 에서 사용에 마운트해서 사용할 공간 만들기
mkdir /www
vi /www/index.html
ysoftman nginx test
# nginx 이미지로 컨테이너 만들기
# run : 컨테이터 생성(실행)
# --name : 컨테이너 이름 설정
# -d : 컨테이너를 백그라운드로 실행
# -i : interactive(stdin open)
# -t : tty 환경
# -v : 호스트 /www 를 컨테이너의 /usr/share/nginx/html 에 readonly 로 마운트, 둘다 절대경를 사용해얖 한다. readonly 를 빼면 디폴트로 쓰기도 가능하다.
# -p : 호스트 포트 8080 을 컨테이너 포트 80 에 바인딩
# nginx : 실행시킬 이미지 이름
docker run --name ysoftman_nginx -dit -v /www:/usr/share/nginx/html:ro -p 8080:80 nginx
# 컨테이너에 명령어 실행하기(쉘 실행하여 접속해보기)
docker exec -u root -it ysoftman_nginx /bin/bash
# 컨테이너 상태 파악
# -v 마운트를 했는데 Config -> Volumes 가 null 이라면 -v 을 사용하지 말자
docker inspect ysoftman_nginx
# 컨테이터의 ip 확인
docker inspect --format='{{.NetworkSettings.IPAddress}}' ysoftman_nginx
# 도커(호스트) ip 확인
# 윈도우의 경우 mingw64 환경(git bash사용)에서 도커가 동작하기 때문에 mingw64 가 호스트가 된다.
# http://도커ip:8080 로 nginx 동작 확인
docker-machine ip
# ysoftman_nginx 컨테이너에서 현재 실행중이 프로세스 보기
docker top ysoftman_nginx
# 컨테이너 목록 보기(STATUS 로 상태를 알 수 있기 때문에 top 대신 사용 가능)
docker ps -a  또는 --all

# 컨테이너 변경사항으로부터 이미지 만들기
# -m "ysoftman_nginx" : 커밋 메시지
# -a "ysoftman" : 작성자
# ysoftman/ysoftman_nginx:1.0 : 저장소:tag 로 이미지 생성
docker commit -m "ysoftman_nginx" -a "ysoftman" ysoftman_nginx ysoftman/ysoftman_nginx:1.0
# dockerfile 을 작성하여 이미지 빌드시
# --no-cache=true 캐시된 이미지를 사용하지 않고 새로 만들어 시간이 올래 걸린다.
# -t 태그 명시(저장소:태그)
# 자세한 내용은 https://docs.docker.com/engine/reference/builder/
docker build --no-cache=true -t ysoftman/ysoftman_nginx:1.0 .
# dockerfile 작성시 유념
# RUN,COPY 실행 마다 레이어가 생성된다.
# 이전 레이어가 실행한것을 알지 못하기 의존성이 있는 명령은 && 연결해 하나의 RUN 으로 실행해야 한다.
# CMD 는 한번밖에 사용하지 못한다.
# CMD "echo aaa; echo bbb" 는 shell(새로운 프로세스 생성) 방식이다.
# CMD ["echo aaaa"] 는 exec(현재 프로세스를 대체) 방식이다.
# exec 방식에서 여러개의 명령을 실행하려면
# CMD ["bash", "-c", "echo aaa; echo bbb;"] 와 같이 bash -c(뒤에 오는 스트링을 쉘명을 처리) 로 실행해야 한다.
# 참고 https://docs.docker.com/engine/reference/builder/#cmd
# 다음은 명령마다 생성된 이미지와 각각의 크기를 알 수 있다.
docker histsory ysoftman/ysoftman_nginx:1.0
# docker hub 로그인(없으면 계정 생성 https://hub.docker.com)
# $HOME/.docker/config.json 설정된 서버로 로그인한다.
docker login
# docker hub 에 이미지 푸시하기
docker push ysoftman/ysoftman_nginx:1.0
# 컨테이너 5초 후 중지
docker stop -t 5 ysoftman_nginx
# 컨테이너 시작하기
docker start ysoftman_nginx
# 컨테이너 로그(비정상 종료 코드로 exit 되는 경우 참고)
# -f, --follow
docker logs -f ysoftman_nginx
# 컨테이너 강제 삭제
docker rm -f ysoftman_nginx
# 컨테이너 전체 삭제
docker rm $(docker ps -aq)
# 이미지 강제 삭제
docker rmi -f ysoftman_nginx:1.0
# 이미지 전체 삭제
docker rmi $(docker images -q)
# 이미지 태깅이 안되거나 이미지 생성 중 실패된 상태의 <none> 이미지 삭제
docker rmi $(docker images -a | grep "<none>" | awk '{print $3}')
# docker 1.13 부터 image container ... 등 관리 명령이 추가되었다.
# 최신 docker 무료버전(ce) 를 설치하자
# 기존 old docker 삭제
sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine
# yum 관련 유틸 설치
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# centos 용 docker 저장소 추가
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# docker ce 설치
sudo yum install docker-ce
# unused 이미지들 모두 삭제
docker image prune
# dangling 이미지들 모두 삭제
sudo docker rmi $(sudo docker images --filter "dangling=true" -q)
# 중지된 컨테이너들 모두 삭제
docker container prune
# exited 또는 created 상태 컨테이너들 삭제
sudo docker ps -a | grep -i -e exited -e created | awk '{print $1}' | xargs docker rm
# 도커 이미지,컨테이너등이 사용하는 디스크 용량 확인
docker system df
docker system df -v

# 사용안하는 이미지, 컨테이너들 모두 삭제

docker system prune

# 100일(2400시간)이 지난 이미지는 모두 삭제
#  -a, --all  Remove all unused images, not just dangling ones
#      --filter filter   Provide filter values (e.g. 'until=<timestamp>')
# -f, --force           Do not prompt for confirmation
docker image prune -a -f --filter "until=2400h"


# 참고
http://pyrasis.com/docker.html
https://hub.docker.com/_/nginx
http://blog.iolo.kr/510
https://docs.docker.com/engine/reference/commandline/dockerd/
https://docs.docker.com/engine/installation/windows/#container-port-redirection
http://www.slideshare.net/pyrasis/docker-fordummies-44424016