docker 공부하면서 case by case로 체계없이 정리한 것들을 기록한 것들이다.

이 내용들은 늘어나거나 줄어들거나 수정될 수 있다는 것을 미리 말해둔다

말그대로 docker를 공부하면서 알게된 단편 지식들을 보관 차원에서 적어둔 것이기 때문이다


1. docker hub에서 이미지를 받을때는 pull 명령을 사용한다. 예를 들어 wildfly의 최신버전(latest)를 받을 경우 다음과 같이 한다.


docker pull jboss/wildfly:latest


2. 내가 받은 이미지들의 목록을 볼 경우 다음과 같이 한다


docker images


3. 이미지 세부 정보를 확인할 때는 inspect 명령어를 사용한다.


docker inspect jboss/wildfly:latest


세부 정보는 json 문자열 형태로 보여지며 주요 정보로는 이미지 ID, 생성일, Docker 버전, 이미지 생성자, CPU 등을 제공한다

정보가 json 문자열 형태로 길게 나오기때문에 만약 특정 정보를 보고자 할때는 다음과 같이 format 옵션을 주어 보고자 하는 항목을 지정하면 그 항목에 대해서만 볼 수 있다. 예를 들어


docker inspect --format="{{ .Os}}" jboss/wildfly:latest


를 사용하면 Os 항목에 대한 값만 알 수 있다. 또 Config 항목의 하위 항목으로 있는 Hostname 항목 값을 알고자 할때는 다음과 같이 한다


docker inspect --format="{{ .Config.Hostname}}" jboss/wildfly:latest


이것은 우리가 json 문자열을 객체로 만들었을때 접근하는 방법을 생각하며 이 명령을 사용하면 된다


4. 이미지를 삭제할때는 다음과 같이 한다


docker rmi jboss/wildfly:latest


5. docker 이미지를 받았으면 이제 이 이미지를 가지고 하나의 컨테이너를 생성해서 실행시키도록 한다. 이해하기 쉽게끔 설명하자면 프로그래밍에서 얘기하는 클래스와 객체로 설명하면 이해하기 쉬울듯 하다.


A라는 클래스를 이용해서 객체를 만들때..


A b = new A();

A c = new A();


이렇게 만들것이다. A라는 클래스를 이용해서 만든 객체 b와 c가 있지만 이 b와 c는 서로를 간섭하지는 않는다. 이것을 docker에 비춰보면 클래스는 이미지가 되고 객체는 컨테이너가 된다. 필요성에 따라 이미지를 이용해서 0개 이상의 컨테이너를 만들수 있지만 이렇게 만들어진 컨테이너들은 서로 독립적인 공간이 되어 서로 간섭하지를 않게 된다


컨테이너의 라이프 사이클은 docker 명령어를 빌려서 설명을 하자면 create(생성) -> start(시작) -> stop(중지) -> rm(삭제) 개념으로 흘러가며 create와 start를 같이 해주는 명령어인 run이 있고 start인 상태에서 중지했다가 다시 시작하는 명령어로 restart가 있다


6. docker에서 컨테이너를 생성하면서 동시에 시작할때는 run을 사용한다. wildfly를 예로 들자면 다음과 같이 한다


docker run -it --name "wildfly" -p 8080:8080 jboss/wildfly:latest


run 명령어를 사용하면서 같이 사용된 옵션에 대해 설명을 하자면 


-i 옵션은 컨테이너 표준 입력을 연다는 뜻이고, -t는 tty(단말 디바이스)를 사용한다는 의미이다. 이 둘을 묶어서 -it로 사용한 것이다. 


--name 옵션을 주면 해당 컨테이너에 대해 특별한 이름을 부여하는 옵션이다. 컨테이너를 start 명령어로 시작하거나 stop 명령어를 이용해서 종료할때 대상 컨테이너를 지정하기 위해 컨테이너 ID를 입력해야 하는데 이 ID라는게 의미가 없는 숫자/문자가 조합된 문자열이라 타이핑하기 어렵다. 이러한 상황을 좀더 쉽게 사용하게 하기 위해 --name 옵션을 주어 의미있는 문자열을 지정함으로써 차후에 start, stop 명령어를 사용할때 그 문자열을 사용하여 해당 명령어를 쉽게 실행할 수 있다.


-p는 호스트와 컨테이너 간의 포트를 연결하는 옵션이다. wildfly는 8080 포트를 이용해서 통신하게 되는데 이것은 어디까지나 컨테이너의 8080 포트를 열은 것이기 때문에 우리가 테스트하기위해 브라우저에서 http://localhost:8080을 입력해도 동작이 이루어지지 않는다. 이로 인해 호스트의 8080 포트를 컨테이너의 8080 포트와 연결해야 제대로 된 동작을 할 수 있다.  한가지 더 첨언을 하자면 컨테이너가 8080 포트를 열었다고 해서 호스트도 반드시 8080 포트를 열으라는 법은 없다. 예를 들어 wildfly 컨테이너를 2개 이상 운영하는데 두 컨테이너가 모두 8080 포트를 열어도 문제가 되는 것은 없다. 그러나 이 두 포트를 모두 호스트의 8080 포트에 연결할 수는 없다. 즉 둘 중 하나의 컨테이너는 -p 8081:8080 이런 식으로 호스트의 8081 포트를 컨테이너의 8080 포트로 열어서 동작하게끔 해야한다


7. 6번의 과정으로 실행을 하게 되면 docker의 foreground로 실행하는 것이기 때문에 이런 식으로의 실행은 추천하지 않는다.  즉 다른 컨테이너를 시작할려면 다시 쉘 창을 열어서 docker 관련 명령을 실행해야 하기 때문이다. 대신 foreground에서 실행시키는 것이기 때문에 컨테이너를 중지할려면 ctrl-c를 눌러 중지할 수 있으며 관련 콘솔 로그 내용도 바로 확인 할 수 있는 장점은 있다. 그러나 docker로 실행되는 것이 WAS나 DB 서버 같은 서버용 프로그램인것을 감안하면 background로 실행되는 것이 좋다


8. 7번에서 설명한대로 background로 컨테이너가 실행되게끔 할려면 -d 옵션을 붙이면 된다


docker run -d --name "wildfly" -p 8080:8080 jboss/wildfly:latest 


6번에서 설명했을때는 -it옵션을 붙였으나 여기서는 background로 실행하는 것이기 때문에 컨테이너 표준입력을 사용할 수가 없다. 그걸 제외한 나머지 옵션은 동일하다

그러나 이렇게 실행하면 6번에서 컨테이너가 보여주는 로그들을 볼 수 없는 문제가 있다. 이를 볼려면 logs 명령어를 사용하면 된다


docker logs -f wildfly


-f 옵션은 linux 에서 tail 명령 사용했을때 -f 사용한 것을 생각하면 된다. 즉 로그를 계속 연속성으로 보여주는 것으로 이해하면 된다. 뒤에 wildfly가 붙은 것은 우리가 docker run을 이용해서 컨테이너를 실행할때 --name 옵션을 이용해서 주었던 그 wildfly이다. 이렇게 컨테이너를 run 명령어를 이용해서 실행할때 --name 옵션으로 별칭 준것을 docker 관련 명령 실행시 특정 컨테이너를 지칭하는데 사용함으로써 편리함을 준다.


9. 처음에 docker를 접하면서 난 docker의 이미지가 프로그램들의 모음(예를 들어 wildfly 컨테이너를 예로 들자면 wildfly와 이를 실행시키기 위해 필요한 부가적인 프로그램(예를 들면 jdk 같은)들의 모음)으로 이해했다. 그러나 docker 관련 구성도를 보면서 이해하게 된것은 컨테이너 안에는 os가 있다는 것이다. 즉 os 안에 관련 프로그램이 설치되어 있는 개념인 것이다. 이 부분은 나중에 build를 공부하면서 확연히 알게 되었다. 즉 기본 베이스 이미지를 centos 같은 리눅스 이미지로 삼은뒤 여기에 apache를 yum 명령어로 설치하면서 이미지 만드는 법을 알게 되었다 물론 베이스 이미지를 centos 같은 os 이미지가 아니라 wildfly 이미지를 사용할 수 있다. 그렇다해도 wildfly에 centos가 이미 있기 때문에 결국 wildfly 이미지에 있는 centos 를 이용하는 것임에는 변함이 없다. 이러한 개념이기 때문에 해당 컨테이너는 독자적인 OS와 그에 따른 환경변수를 가지고 실행할 수가 있는것이며 서로간의 컨테이너에 영향을 주지 않고 독립적으로 움직이게 되는 것이다.


docker 이미지는 os가 포함이 되어 있는 0개 이상의 프로그램이 설치되어 있는 이미지이다


10. 9번에서 설명했다시피 이미지는 자체적으로 OS가 포함되어 있다고 했다. 그렇기 때문에 우리는 컨테이너가 실행중일때 이 컨테이너를 구동시키는 기반이 되는 OS에 접속할 수가 있다. 즉 우리가 wildfly 이미지를 이용하여 컨테이너를 실행중일때 이 컨테이너의 OS에 연결할 수 있다는 것이다. 이것은 나름 작업(?)적인 차원에서 중요한 의미가 있다. 예를 들어 우리가 wildfly에서 구동중인 web application을 만든다고 가정해보자. 그러면 파일 업로드 기능을 넣어야 하는데 파일 업로드를 하게 되면 OS에서 관리되는 디렉토리 중 하나에 사용자가 업로드한 파일이 있어야 하며 그러한 디렉토리를 만들어놔야 한다. 그러면 이 디렉토리를 만들려면 wildfly 컨테이너의 os 쉘에 접근해서 디렉토리를 만드는 명령(mkdir)을 실행해야 한다. 이러한 OS 쪽의 작업을 하거나 또는 wildfly의 설정파일(예를 들면 standalone.xml)을 수정할 수 있다. 그리고 이렇게 변경이 된 내용이 있는 컨테이너를 docker commit 명령을 이용해서 새로운 이미지로 만들어 이를 다른 사람에게 배포할 수 있다.


서론이 길었는데 이 기능을 수행하는것에 대해 설명하도록 하겠다

먼저 경우를 분리해서 생각해야 할 부분이 있는데 우리가 컨테이너를 실행시킬때 /bin/bash 같은 쉘을 실행시킨 상황과 그렇지 않은 상황으로 분리해야한다. /bin/bash를 실행시켜서 현재 컨테이너가 bash shell이 실행중인 상황이면 docker의 attach 명령을 이용해서 접속하면 된다


docker attach wildfly


이러면 bash shell 에 접속하여 shell 명령어를 실행시킬수 있다. 여기서는 컨테이너 이름을 지칭하기 위해 wildfly를 썼지만 실제로 wildfly는 이렇게 명령해도 접속이 되질 않는다. 왜냐면 wildfly는 bash shell을 실행시키지 않고 컨테이너를 띄우기 때문이다. 때문에 attach 명령으로 접속 시도를 해도 동작이 되질 않는다. 또한 백그라운드로 실행시킨 컨테이너의 경우도 마찬가지이다(왜 그런지는 곰곰히 생각해보면 이해하기 쉽다. 우리가 background로 컨테이너를 실행시킨다는건 docker 명령어를 실제로 내리는 shell 창에서 억세스하는 것이 아니라 말그대로 background로 돌리는 것인데 거기서 shell 명령을 실행시키는 /bin/bash가 실행될리가 없잖은가?) 이렇게 /bin/bash 를 실행시키지 않고 시작한 컨테이너나 또는 background에서 실행중인 컨테이너를 접속하기 위해서는 exec 명령을 사용해서 /bin/bash를 실행시켜 접속하면 된다


docker exec -it -u root wildfly /bin/bash


이렇게 exec 명령을 통해 /bin/bash를 실행시켜서 bash shell 명령을 실행시킬수가 있게 된다. -u는 os의 어떤 계정을 통해서 명령을 실행시킬것인지를 지정하는 것이다. 그러면 어떤 계정을 사용해야 하는가? 그것은 해당 컨테이너의 docker 이미지를 build 하는데 사용된 Dockerfile의 소스를 보면 알 수 있다. 


일반적으로 docker 이미지들은 github 에서 이미지 관련 파일들이 존재하여 이를 github에서 빌드하여 docker hub에 배포되는 형태를 취하고 있다. 그래서 이 Dockerfile의 소스를 볼려면 github을 봐야한다. wildfly를 예로 들면 docker hub의 wildfly에 대한 페이지(https://hub.docker.com/r/jboss/wildfly/)를 가보면 Source 항목에 github과 링크되어 있는 링크를 볼 수 있다. 이를 클릭하면 github에 있는 해당 이미지를 빌드하는데 사용된 Dockerfile을 볼 수 있게 된다. 이 Dockerfile 을 클릭해서 상세 내용을 보면 USER 란 항목이 있어 Dockerfile을 이용해 빌드하면서 실행하게 되는 명령어를 수행하는 OS 계정을 언급하게 된다. 바로 이 USER 항목에 언급된 계정을 -u 옵션 뒤에 사용해주면 된다.  모든 docker 이미지가 root 계정을 사용하는 것은 아니기 때문에 이것을 확인하고 진행하기 바란다.


attach와 exec에 대한 추가 설명

위의 내용을 읽어보면 bash shell 기능을 위주로 설명했기때문에 현재 bash shell이 실행중인 컨테이너를 접속하여 할때는 attach, bash shell 이 실행중이지 않은 컨테이너를 접속하려 할때는 exec를 사용하는 것으로 이해가 될 것 같아서 부가설명을 하고자 한다.


attach는 현재 실행중인 컨테이너에 접속 한다고 보는 것이 맞다. docker 이미지가 컨테이너로 생성되면서 run이 될때 이미지 가 내부적으로 특정 프로그램을 실행시킬수도 있고 또는 run 명령을 내리는 시점에서 특정 프로그램을 실행 시키게도 할 수 있다. 이때 bash shell 이 실행되도록 /bin/bash 를 실행시켜서 attach 시 바로 bash shell prompt가 보이게끔 할 수도 있다. 그러나 그렇지 않은 이미지도 있다. 예를 들어 위에서 언급했던 wildfly 이미지의 경우 이 이미지는 컨테이너로 올라가는 시점에 wildfly의 standalone.sh 스크립트가 실행시켜 wildfly가 구동되도록 하고 있기 때문에 콘솔 화면에서 wildfly의 로그가 올라오는 것을 볼 수 있다. 그래서 docker의 attach 명령을 이용해서 wildfly의 컨테이너를 접속하면 wildfly 구동되고 있는 상황에서 나오는 콘솔로그를 볼 수 있다. 또한 이 상태에서 ctrl-c를 클릭하면 wildfly 가 실행되는 것이 종료되는 것을 볼 수 있다.


이에 반해 exec는 현재 실행중인 컨테이너에 특정 shell script를 실행하는 것이라고 보는게 맞다. 예를 들어 wildfly 이미지의 컨테이너가 wildfly란 이름으로 컨테이너가 실행되어 있는 상태에서 docker exec -it wildfly /bin/bash 로 명령을 내리면 해당 컨테이너에 bash shell을 실행시키는 것이다. 그래서 bash shell prompt가 화면에 나오게 되는 것이다. 만약 /bin/bash가 아는 사용자가 만든 별도 스크립트를 실행시키면 그 스크립트가 실행시키는 것이다(exec 명령어를 사용할때 -it 옵션을 주었기 때문에 해당 명령의 실행 결과가 현재 보는 화면에 바로 보이게 된다)


11. docker 에서 등록되어 있는 컨테이너 목록을 확인 하는 명령은 다음과 같이 한다


docker ps -a 


-a 옵션을 빼고 docker ps 로 실행하게 되면 현재 동작중인 컨테이너만 보여주기 때문에 중지되어 있는 컨테이너는 보여주지 않게 된다. 그래서 -a 옵션을 항상 붙여서 중지되어 있는 것도 같이 확인하는 버릇(?)을 들이는 것이 좋다


12.  docker에서 실행중인 각 컨테이너가 사용하는 CPU 및 메모리 점유율 등 컨테이너의 상태를 확인하기 위해서는 다음의 명령을 사용한다


docker stats


13. 중지되어 있는 컨테이너를 구동하는 명령은 다음과 같이 한다


docker start wildfly


14. 실행중인 컨테이너를 중지시키는 명령은 다음과 같이 한다.


docker stop wildfly


15. 컨테이너를 재시작할때의 명령은 다음과 같이 한다


docker restart wildfly


16. 컨테이너를 삭제할때의 명령은 다음과 같이 한다


docker rm -f wildfly


원래 컨테이너를 삭제할때는 현재 상태가 중지되어 있는 컨테이너만 삭제가 가능하기 때문에 실행중인 컨테이너는 삭제를 할 수가 없다. 그러나 -f 옵션을 붙이면 실행중인 컨테이너도 삭제하게 된다

현재 컨테이너에 등록되어 있는 모든 컨테이너를 삭제하고자 할 때는 다음과 같이 한다


docker rm $(docker ps -a -q)


여기에 위에서 설명한 -f 옵션을 붙이면 실행중인 컨테이너까지 모두 삭제하게 된다


17. 현재 구동중인 컨테이너에서 실행 중인 프로세스를 모두 일시정지 시키고자 할 때는 다음과 같이 한다


docker pause wildfly


일시정지된 컨테이너를 docker ps 명령어를 이용해서 컨테이너 목록으로 확인해보면 Status에 Paused로 나온다(참고로 stop 명령을 이용해서 컨테이너를 중지시킨 경우 docker ps 명령을 이용해 목록을 확인해보면 Status에 Exited로 나온다)

이렇게 일시정지된 컨테이너를 다시 재시작 시킬때는 다음과 같이 한다


docker unpause wildfly


18. 현재 구동중인 컨테이너에서 실행중인 프로세스를 확인할때는 다음과 같이 한다


docker top wildfly


linux에서 top 명령을 실행한것과 같은 유형의 결과물을 보여준다


19. 구동중인 컨테이너에서 실행중인 프로세스가 이용중인 포트를 확인하고자 할때는 다음과 같이 한다


docker port wildfly


이렇게 하면 우리가 위에서 실행한 wildfly의 경우 다음과 같이 나타날 것이다


8080/tcp ->0.0.0.0:8080


이것은 컨테이너의 8080 포트(왼쪽의 8080/tcp)를 호스트 컴퓨터(정확하게는 현재 내가 사용중인 OS)의 8080 포트(오른쪽의 0:.0.0.0:8080)와 연결되고 있음을 의미한다.


20. 컨테이너와 호스트 컴퓨터 간의 파일 복사는 다음과 같이 한다


docker cp wildfly:/opt/jboss/wildfly/standalone/configuration/standalone.xml D:/dockerimages


위의 명령은 wildfly 컨테이너의 /opt/jboss/wildfly/standalone/configuration 디렉토리에 있는 standalone.xml 파일을 호스트 컴퓨터의 D:/dockerimages  디렉토리에 복사한다는 뜻이다. 

명령어의 구조가 docker cp 복사대상파일 복사되는위치 구조이기 때문에 이 구조만 맞춰주면 마찬가지로 호스트에서 컨테이너로 파일 복사를 할 수 있다. 위에서 언급한 명령을 호스트에서 컨테이너로 복사하는 개념으로 바꿔서 한다면 다음과 같이 한다


docker cp D:/dockerimages/standalone.xml wildfly:/opt/jboss/wildfly/standalone/configuration


이렇게 하면 호스트 컴퓨터의 D:/dockerimages/standalone.xml 파일을 wildfly 컨테이너의 /opt/jboss/wildfly/standalone/configuration 디렉토리에 복사하게 된다. 명령어를 유심히 보면 알겠지만 컨테이너의 디렉토리나 디렉토리에 있는 파일을 언급할때는 컨테이너 이름과 컨테이너의 디렉토리 또는 파일 사이에 :(콜론)을 붙인다(wildfly:/opt/jboss/wildfly/standalone/configuration/standalone.xml)


21. 기본적으로 Docker에서 실행되는 명령은 OS의 root 계정으로 실행이 된다(그도 그럴것이 Dockerfile에서 ENV를 이용해서 OS의 환경변수도 설정이 가능하기 때문에 이런것이 가능할려면 root 계정일수 밖에 없다) 그러나 root 계정이 아닌 다른 계정을 만들어서 명령을 실행시켜야 할 수도 있다. 이럴땐 Dockerfile에서 해당 계정을 만든뒤 USER 명령을 이용해서 해당 계정으로 바꾼다. 그러면 그 후에 실행되는 것은 해당 계정으로 실행하는 것이 된다. 


22. 현재 실행중인 컨테이너를 이미지로 만들어야 할 수 있다. 이미지로 제공되는 설정을 컨테이너로 실행되게 한 상태에서 해당 설정을 바꾸고 이를 이미지로 저장해서 재사용할 수도 있기 때문이다. 이럴때는 commit 명령을 이용한다. 예를 들어 wildfly란 이름의 컨테이너를 terry/wildfly 란 이미지로 저장하고자 할 때에는 docker commit -a "Terry Chang" wildfly terrychang/wildfly 로 명령을 실행한다. 이때 a 옵션은 이미지를 제작한 제작자를 설정하는 옵션이다.


23. 이미지를 특정 파일로 백업하고 특정 파일을 이미지로 복구할때 save/load 명령을 사용할 수 있다. 이와 비슷한 것으로 export/import 가 있다. 이 둘의 차이는 save/load는 그 대상이 이미지인것에 반해 export/import는 그 대상이 컨테이너(엄밀하게 말하면 export 명령을 이용해서 컨테이너를 특정 파일로 백업한뒤에 import를 이용해서 그 특정 파일을 이미지로 복구하는 것이다. 이 부분 헷갈리지 말도록)이다.

예를 들어 terrychang/wildfly란 이미지를 D:/dockerimages/customwildfly.tar 파일로 백업할때는 다음과 같이 한다.


docker save -o D:/dockerimages/customwildfly.tar terrychang/wildfly


이와는 반대로 D:/dockerimages/customwildfly.tar 파일을 terrychang/wildfly란 이미지로 복구할때는 다음과 같이 한다


docker load -i d:\dockerimages\customwildfly.tar


24. docker 이미지를 빌드할때는 빌드할때 필요한 파일들만 들어가 있는 디렉토리에 Dockerfile을 넣고 빌드해야 한다. 이게 나름 중요한데 Docker는 이미지를 빌드 할때 빌드명령이 실행된 디렉토리에 있는 모든 파일을 특정 공간에 업로드하고 사용한다. 이 과정을 몰라서 Dockerfile 파일을 가상 리눅스 머신 파일이 있는 곳에 넣고 빌드를 하다보니 빌드랄 할때 가상 리눅스 머신 파일이 같이 올라가면서 이 올라가는 과정만 계속 하다가 결국 뻗어버리는 증상이 있었다. 그래서 빌드를 할때는 Dockerfile 파일과 Dockerfile 안에서 빌드할때 사용되는 파일 및 디렉토리만 있게 하고 빌드 명령을 실행시켜야 한다.


25. centos나 ubuntu docker 이미지 파일의 경우 경량화(?)를 하는 이유로 백그라운드 서비스를 관리하는 systemd가 빠져있다. 그래서 centos의 경우는 centos에서 배포하는 centos/systemd 이미지를 사용하면 systemd를 이용한 백그라운드 서비스 관리를 이용할 수 있다. 이때 /usr/sbin/init 을 실행해야 한다. 이 init이 pid가 1이어야 systemd를 이용할 수 있다. 문제는 init을 실행하면서 별도 shell script(a.sh 라고 가정하자)를 실행해야 하는 상황이 있을수가 있는데 그럴때는 별도 shell script 파일(b.sh 라고 가정하자)을 만들고 이 파일에서 /usr/sbin/init과 a.sh가 실행이 되도록 하면 된다. 이때 주의할 점이 있는데 반드시 /usr/sbin/init 이 가장 마지막에 실행되도록 shell script를 만들어야 한다. 나는 pid가 1이어야 한다고 생각해서 /usr/sbin/init을 가장 먼저 실행되게끔 했는데 이렇게 하니까 b.sh가 실행이 되지 않는 문제가 발생하였다.  다음은 wildfly의 standalone.sh와 /usr/sbin/init이 실행되게끔 한 shell script 파일 소스이다.


#!/bin/bash

# background로 wildfly를 실행시킨다. 
# 원래는 /opt/jboss/wildfly/standalone/log/server.log 파일에 로그를 기록하지만 
# 별도 로그파일을 만들어서 보고 싶을 경우엔 
# /dev/null 부분을 특정 로그(ex: /usr/log/wildfly.log)로 지정하면 그 파일에 로그를 기록하게 된다

nohup /opt/jboss/wildfly/bin/standalone.sh -b 0.0.0.0 -bmanagement 0.0.0.0 > /dev/null &
exec /usr/sbin/init


26. 25번의 연장선상에 해당되는 설명인데 /usr/sbin/init을 실행할 경우 Dockerfile의 ENV를 이용해 설정한 환경변수가 이미지에 적용이 되지 않는다. 원래 ENV를 이용해서 환경변수를 설정하면 이것이 빌드할때 이미지에 반영되어 이 이미지를 컨테이너로 실행시킬때 해당 환경변수가 OS에 적용이 되는데 /usr/sbin/init이 실행되면 이 기능이 되지를 않는다(리눅스를 몰라서 구체적으로 설명하기는 어렵지만 구글링을 해보면 이 증상은 당연한 것이라고 나온다) 그래서 이와 같이 /usr/sbin/init이 실행되는 이미지가 컨테이너로 실행이 되는 상황에서 환경변수가 인식이 되게끔 할려면 /etc/environment 파일에 해당 환경변수가 기록이 되게끔 Dockerfile에 서술해주면 된다. 예를 들어 다음의 코드를 Dockerfile 안에 넣어주면..


RUN echo -e "LANG=ko_KR.utf8\nLC_ALL=\nJAVA_HOME=/opt/java" > /etc/environment


/etc/environment 파일이 다음과 같이 된다. 


LANG=ko_KR.utf8
LC_ALL=
JAVA_HOME=/opt/java


그러나 위의 내용은 Dockerfile을 통해 빌드를 해서 이미지를 만든뒤 컨테이너로 올려 실행할때의 발생되는 문제이지 Dockerfile에서 ENV로 설정한 환경변수를 빌드하는 과정에서 해당 환경변수를 이용할때는 문제가 없다. 예를 들어 ENV JAVA_HOME=/opt/java 로 했다고 가정할 경우 Dockerfile에서 이 환경변수를 이용하기 위해 RUN mv jdk* ${JAVA_HOME} 로 했을 경우 JAVA_HOME은 /opt/java로 인식되어 mv 명령을 수행하게 된다. 즉 빌드하는 과정에서 ENV로 설정한 환경변수를이용하는데는 문제가 없으니 오해없길 바란다.  


27. 25,26번의 연장선상에 해당되는 내용인데 26번에서 /usr/sbin/init을 실행하면 ENV로 설정한 환경변수가 이미지에 적용이 안되어 컨테이너에서 이 환경변수를 이용할 수 없다고 했다. 이런 문제때문에 환경변수를 이용해 지정하게 되는 PATH 또한 이 영향을 받게 된다. 이 부분을 해결할려면 /etc/profile.d 디렉토리에 별도의 shell script 파일이 생성되게끔 해서 컨테이너가 시작될때 이 shell script가 자동으로 실행이 되도록 한다. 예를 들어 Dockerfile에 ENV JAVA_HOME=/opt/java 을 넣고 다음의 코드를 Dockerfile에 넣어주면

 

RUN echo -e "export PATH=$PATH:$JAVA_HOME/bin" > /etc/profile.d/path.sh


빌드하는 시점에 $JAVA_HOME은 /opt/java로 인식이 되어 /etc/profile.d/path.sh 파일엔 export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/java/bin 가 들어가게 된다.


28. github에서 빌드 관련 파일들을 올려두고 Docker Hub와 연동해서 이미지 빌드 작업을 진행 할 경우 간혹 이런 메시지를 볼 수 있다


/assets/setup.sh: /bin/bash: bad interpreter: Text file busy


로칼에서 빌드할때는 이런 에러가 없었는데 Docker Hub로 연동해서 빌드할때 이런 에러 메시지가 나온다. 구글링을 한 바로는 이럴때 해당 shell 스크립트 파일을 실행하기 전에 sync 명령어를 실행하라고 해서 이 에러메시지를 잡았다. 

위의 에러 메시지가 나타나게 된 Dockerfile 코드를 예를 들어 구체적으로 설명하자면 에러 메시지가 나올당시의 Dockerfile 코드는 다음과 같이 되어 있었다


RUN chmod +x /assets/setup.sh && /assets/setup.sh


위의 코드를 다음과 같이 에러 메시지를 유발시키는 shell script(여기서는 /assets/setup.sh) 파일 앞에 sync를 추가로 넣어 실행이 되게끔 했다.


RUN chmod +x /assets/setup.sh && sync && /assets/setup.sh


이렇게 코드를 수정한뒤 빌드해서 에러 없이 빌드를 마쳤다

저작자 표시 비영리 변경 금지
신고

'프로그래밍 > Docker' 카테고리의 다른 글

Hyper-V에 CoreOS 설치하기(2)  (0) 2017.11.12
Hyper-V에 CoreOS 설치하기(1)  (0) 2017.11.12
docker에 대한 형식없는 정리  (0) 2017.07.19

트랙백을 확인할 수 있습니다

URL을 배껴둬서 트랙백을 보낼 수 있습니다

다른 카테고리의 글 목록

프로그래밍/Docker 카테고리의 포스트 목록을 보여줍니다