docker registry로 도커 컨테이너 관리하기

[markdown]
docker-registry

보통은 서버의 형상관리를 chef로 관리하지만,
소스를 배포할 때에 하나의 이미지로 뭉쳐서 배포를 해보고 싶어서
docker를 도입해보려고 공부중이다.

그런데 내가 생성한 이미지를 docker hub라는 열린 docker registry
올릴 수는 없어서, 찾아본것이 docker registry였다.

책이나 블로그에서 그다지 심도있게 다루고 있지 않아서, 쉬운가보다 했는데,
왠걸 설치가 생각보다 까다로웠다.

디지털오션의 글이 정말 많이 도움이 됐는데, 거기 있는 글을 보고
내용을 조금 추가, 수정 한것이라 보시면 될 것 같다.

모쪼록 docker registry를 도입하고자 하는 사람이 이 글을 보고 조금이나마 쉽게
구축 할 수 있으면 좋겠다.

시작하기 전에…

  • ubuntu를 사용 (14.04 면 좋음)
  • docker와 docker compose는 설치가 되어 있어야함
  • docker registry에서 사용할 도메인이 있어야함

docker 설치

설치전에 해야할 것들

뭔가 귀찮은게 많이 있는데, 그냥 빨리 설치하고 싶은 사람은 아래에 설.치. 부분으로 바로 이동하자.
시간이 있는 사람들은 봐두면 나중에 도움이 되는 부분은 있을 것 같다.

[code lang=text]
$ sudo apt-get update
$ sudo apt-get install apt-transport-https ca-certificates
$ sudo apt-key adv –keyserver hkp://p80.pool.sks-keyservers.net:80 –recv-keys 58118E89F3A912897C070ADBF76221572C52609D
$ sudo /etc/apt/sources.list.d/touch docker.list
[/code]

우분투의 다른 버전이라면 docker.list파일에 아래의 내용으로 변경하자

[code lang=text]
### ubuntu 12.04
deb https://apt.dockerproject.org/repo ubuntu-precise main

### ubuntu 14.04
deb https://apt.dockerproject.org/repo ubuntu-trusty main

### ubuntu 15.10
deb https://apt.dockerproject.org/repo ubuntu-wily main
[/code]

[code lang=text]
### `APT` 패키지의 인덱스를 업데이트
$ sudo apt-get update

### 오래된 버전 삭제
$ sudo apt-get purge lxc-docker

### `APT`확인하기
$ sudo apt-cache policy docker-engine
[/code]

linux-image-extra 설치

[code lang=text]
$ sudo apt-get install linux-image-extra-$(uname -r)
$ sudo apt-get install apparmor
$ sudo apt-get install docker-engine
[/code]

설.치.

위에 주저리 주저리 많은데, 보통은 이것만 해도 설치가 된다.

[code lang=text]
$ sudo apt-get update
$ sudo apt-get install docker-engine
$ sudo service docker start
$ sudo docker run hello-world
[/code]

요즘에 도커를 설치할 일이 많아서, 아래의 링크에 설치 스크리트를 정리해 두었다.

도커 설치 스크립트

docker compose 설치

[code lang=text]
$ sudo apt-get -y install python-pip
$ sudo pip install docker-compose
[/code]

docker compose 테스트

[code lang=text]
$ cd ~/docker-registry/
$ mkdir hello-world
$ cd hello-world
$ vi docker-compose.yml
[/code]

docker-compose.yml을 아래와 같이 만들자.

[code lang=text]
test-compose:
image: hello-world
[/code]

테스트

[code lang=text]
$ sudo docker-compose up
[/code]

아래와 같이 나오면 성공이다.

[code lang=text]
Creating helloworld_test-compose_1
Attaching to helloworld_test-compose_1
test-compose_1 |
test-compose_1 | Hello from Docker.
test-compose_1 | This message shows that your installation appears to be working
………. 블라블라……………..
test-compose_1 |
helloworld_test-compose_1 exited with code 0
[/code]

docker-registry 설치에 docker-compose가 이용되는 것이므로
docker-compose에 대해 더 알고 싶은 사람은 아래 링크를 참고하자.

Docker compose

서버와 클라이언트의 버전이 다르다고 나올 때

아래와 같은 메세지가 나오는 경우가 있는데,
아래의 경우 클라이언트가 서버보다 새로운 버전이라 나는 에러이니
설치된 오래된 docker를 삭제하고 새로운 버전으로 다시 설치하자.

[code lang=text]
ERROR: client and server don't have same version (client : 1.21, server: 1.18)
[/code]

아래의 메세지로 도커에 대한 정보를 알 수 있다.

[code lang=text]
$ docker version
$ docker info
[/code]

패스워드 생성을 위한 패키지 설치

apache2-utils에 있는 htpasswd 를 사용하기위한 패키지 다운로드

[code lang=text]
### kr.archive.ubuntu.com 에서는 패키지를 잘 못받아 오는 경우가 있으므로 변경해줌
$ sudo sed -i 's/kr.archive.ubuntu.com/ftp.daum.net/g' /etc/apt/sources.list
$ sudo apt-get update
$ sudo apt-get -y install apache2-utils
[/code]

Docker Registry 설치 및 설정하기

두개의 컨테이너를 연결할때 docker-compose를 사용할 것이다.
docker-compose.yml에 정의 된 내용을 설정 파일로 사용한다.

도커 레지스트리에서 사용할 디렉토리를 생성한다.

[code lang=text]
$ mkdir ~/docker-registry && cd $_
$ mkdir data
[/code]

docker-registry를 만들기위한 docker-compose.yml 파일을 아래와 같이 생성한다.

[code lang=text]
registry:
image: registry:2
ports:
– 127.0.0.1:5000:5000
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
volumes:
– ./data:/data
[/code]

아래의 명령어로 docker-registry 를 띄우자.

[code lang=text]
$ cd ~/docker-registry
$ sudo docker-compose up
[/code]

아래와 같은 메세지들이 나온다.

[code lang=text]
Attaching to dockerregistry_registry_1
registry_1 | time="2016-02-19T02:34:50Z" level=warning msg="No HTTP secret provided – generated random secret. This may cause problems with uploads if multiple registries are behind a load-balancer. To provide a shared secret, fill in http.secret in the configuration file or set the REGISTRY_HTTP_SECRET environment variable." go.version=go1.5.3 instance.id=840ceb15-6c68-41b2-a793-967ee3974a20 version=v2.3.0
registry_1 | time="2016-02-19T02:34:50Z" level=info msg="redis not configured" go.version=go1.5.3 instance.id=840ceb15-6c68-41b2-a793-967ee3974a20 version=v2.3.0
registry_1 | time="2016-02-19T02:34:50Z" level=info msg="using inmemory blob descriptor cache" go.version=go1.5.3 instance.id=840ceb15-6c68-41b2-a793-967ee3974a20 version=v2.3.0
registry_1 | time="2016-02-19T02:34:50Z" level=info msg="listening on [::]:5000" go.version=go1.5.3 instance.id=840ceb15-6c68-41b2-a793-967ee3974a20 version=v2.3.0
registry_1 | time="2016-02-19T02:34:50Z" level=info msg="Starting upload purge in 55m0s" go.version=go1.5.3 instance.id=840ceb15-6c68-41b2-a793-967ee3974a20 version=v2.3.0
[/code]

No HTTP secret provided라는 문구에 놀라지 말라고 한다. 정상이라고~

아무튼 저 메세지가 나오면 잘 된것이니 Ctrl + C를 눌러서 빠져 나가자.
registry 하나 올리려고 docker-compose를 깐게 아니다.

Nginx 설정하기

[code lang=text]
$ mkdir ~/docker-registry/nginx
[/code]

docker-compose.yml파일을 다시 열어서 아래의nginx 관련 설정을 추가하자.

[code lang=text]
nginx:
image: "nginx:1.9"
ports:
– 5443:443
links:
– registry:registry
volumes:
– ./nginx/:/etc/nginx/conf.d
[/code]

결과적으로 docker-compose.yml은 아래와 같은 형태가 된다.

[code lang=text]
nginx:
image: "nginx:1.9"
ports:
– 5443:443
links:
– registry:registry
volumes:
– ./nginx/:/etc/nginx/conf.d
registry:
image: registry:2
ports:
– 127.0.0.1:5000:5000
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
volumes:
– ./data:/data
[/code]

그런데 nginx의 설정파일이 빠져있으므로 추가하자.

nginx 설정파일 추가하기

아래의 경로에 설정파일을 추가하자.

[code lang=text]
$ vi ~/docker-registry/nginx/registry.conf
[/code]

설정은 아래와 같다.

[code lang=text]
### registry.conf

upstream docker-registry {
server registry:5000;
}

server {
listen 443;
server_name docker.gyus.me;

client_max_body_size 0;
chunked_transfer_encoding on;

location /v2/ {
### docker 1.5 버전 이하에서 접속 안되게 함.
if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
return 404;
}

proxy_pass http://docker-registry;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}
}
[/code]

$ docker-compose up으로 서버를 올릴 때 아래와 같은 에러가 났는데,

[code lang=text]
ERROR: failed to register layer: Untar re-exec error: unexpected EOF: output:
[/code]

sudo로 권한을 높여주니 해결 됐다. 압축을 풀 권한이 없는 곳에 압축을 풀려다가 에러가 난것 같다.

[code lang=text]
$ sodo docker-compose up
[/code]

nginx 랑 잘 연동이 되었는지 curl로 확인해보자.

[code lang=text]
$ curl http://localhost:5000/v2/
[/code]

{} 로 결과 값이 나오면 성공이다.

https가 바인딩되는 포트로 사용될 5443 포트로도 잘 되는지 확인해 보자.

[code lang=text]
$ curl http://localhost:5443/v2/
[/code]

역시나 {} 로 결과 값이 나오면 성공이다.

인증 설정하기

SSL만으로 불안한 사람은 이걸 하는게 좋은 것 같다.
나는 귀찮아서 사실 패스했다.

htpasswd 파일을 생성해보자.

[code lang=text]
$ cd ~/docker-registry/nginx
$ htpasswd -c registry.password <유저명>

### 패스워드를 입력해야한다. (안입력해도 되긴된다.)
New password:
Re-type new password:
[/code]

nginx설정파일인 registry.conf 파일에 인증관련 설정을 추가해주자.
location 항목 아래에 설정해주어야한다.

[code lang=text]
location /v2/ {
#### docker 1.5 버전 이하에서 접속 못하도록 막기.
if ($http_user_agent ~ "^(docker\1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
return 404;
}

#### 인증 설정
auth_basic "registry.localhost";
auth_basic_user_file /etc/nginx/conf.d/registry.password;
add_header 'Docker-Distribution-Api-Version' 'registry/2.0' always;

proxy_pass http://docker-registry;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}
[/code]

SSL 세팅하기

nginx 설정파일인 ~/docker-registry/nginx/registry.conf 파일에 SSL 인증 관련 설정을 추가하자.
SSL인증을 위해서는 해당도메인의 인증서 파일인 crt파일과 key파일이 필요하다.

[code lang=text]
server {
listen 443;
server_name deploy.gyus.me;

# SSL
ssl on;
ssl_certificate /etc/nginx/conf.d/domain.crt;
ssl_certificate_key /etc/nginx/conf.d/domain.key;
[/code]

인증서와 키파일은 startssl 사이트에서 공짜로 얻을 수도 있고,
다른 인증기관에서 구매도 가능하다.

임시적으로 사용하기 위해서 자가 인증서를 발급 하는 방법도 있는데, 아래에서 알아보자.

자가 인증서 발급하기

도커에서는 자가인증서만 가지고는, SSL를 사용할 수가 없고
자가 인증서를 인증해주는 자가 인증기관도 우리 서버에 세팅을 해야한다;;

자가인증기관에서 인증해줄수 있도록 루트 인증키를 만들자.

[code lang=text]
$ cd ~/docker-registry/nginx
### 루트 키를 만들자.
$ openssl genrsa -out rootCA.key 2048
### 루트 인증키를 만들자. (물어보는거는 대충 알아서 잘 적도록하자.)
$ openssl req -x509 -new -nodes -key rootCA.key -days 10000 -out rootCA.crt
[/code]

이제 우리 서버의 인증서를 만들어보자.

[code lang=text]
### 키파일을 만든다.
$ openssl genrsa -out gyusme.key 2048
### 인증서 서명 요청 파일을 만듬 (csr)
$ openssl req -new -key gyusme.key -out gyusme.csr
### 인증서를 생성
$ openssl x509 -req -in gyusme.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out gyusme.crt -days 10000
[/code]

인증서 서명 요청 파일 (csr)을 만들때에 이것 저것 물어보는데, 다른 건 몰라도 도메인은 잘 입력해주도록 하자.
그리고 패스워드를 물어보면 그냥 엔터 를 치자

[code lang=text]
Country Name (2 letter code) [AU]:KO
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:Busan
Organization Name (eg, company) [Internet Widgits Pty Ltd]:gyus,inc.
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:docker.gyus.me
Email Address []:
A challenge password []:
An optional company name []:
[/code]

여기까지 했다면 nginx디렉토리에는 아래와 같은 파일들이 있을 것이다.

[code lang=text]
-rw-rw-r– 1 ubuntu ubuntu 1229 Feb 19 17:29 gyusme.crt
-rw-rw-r– 1 ubuntu ubuntu 1054 Feb 19 17:28 gyusme.csr
-rw-rw-r– 1 ubuntu ubuntu 1679 Feb 19 17:26 gyusme.key
-rw-rw-r– 1 ubuntu ubuntu 831 Feb 19 17:10 registry.conf
-rw-rw-r– 1 ubuntu ubuntu 1269 Feb 19 17:25 rootCA.crt
-rw-rw-r– 1 ubuntu ubuntu 1675 Feb 19 17:25 rootCA.key
-rw-rw-r– 1 ubuntu ubuntu 17 Feb 19 17:29 rootCA.srl
[/code]

우리의 서버가 인증기관인것 처럼 동작하게 하는 세팅을 해주자.

[code lang=text]
$ sudo mkdir /usr/local/share/ca-certificates/docker-cert
$ sudo cp rootCA.crt /usr/local/share/ca-certificates/docker-cert
$ sudo update-ca-certificates
[/code]

SSL 테스트하기

테스트를 해보려면, SSL 설정까지 마치고 에러없이 docker registrynginx가 올라가야 한다.

[code lang=text]
$ cd ~/docker-registry
$ docker-compose up
[/code]

[code lang=text]
### 기본인증 사용X, 자가 인증서 X 인 경우
$ curl https://domain:5443/v2/

### 기본인증 사용 O, 자가 인증서 X 인 경우
$ curl https://USERNAME:PASSWORD@domain:5443/v2/

### 기본인증 사용 O, 자가 인증서 O 인 경우
$ curl https://USERNAME:PASSWORD@domain:5443/v2/ -k
[/code]

도커 레지스트리 서비스로 등록하기

도커 레지스트리를 운영하는 서버를 재시작 할 경우가 생길 수 있는데,
이때에 필요한 프로세스를 일일이 찾아서 올리는거는 많은 정신력을 필요로 한다.

그런일을 당하지 않도록 서비스로 등록해보자.

Upstart 스크립트를 작성해서, 시스템이 부팅될 때 시작되도록 해보자.

[code lang=text]
$ cd ~/docker-registry
### 지금 올라간 컨테이너를 삭제
$ docker-compose rm
$ sudo mv ~/docker-registry /docker-registry
$ sudo chown -R root: /docker-registry
[/code]

Upstart스크립트를 작성해보자.

[code lang=text]
$ sudo vi /etc/init/docker-registry.conf
[/code]

[code lang=text]
### docker-registry.conf
description "Docker Registry"

start on runlevel [2345]
stop on runlevel [016]

respawn
respawn limit 10 5

chdir /docker-registry

exec /usr/local/bin/docker-compose up
[/code]

이제 docker-registry를 실행 시켜보자.

[code lang=text]
$ sudo service docker-registry start
[/code]

아래와 같이 나오면 성공이다.

[code lang=text]
docker-registry start/running, process 27241
[/code]

아래의 명령어로 또한 확인이 가능하다.

[code lang=text]
$ docker ps
[/code]

로그는 아래의 명령어로 확인이 가능하다.

[code lang=text]
$ sudo tail -f /var/log/upstart/docker-registry.log
[/code]

curl 명령어로 확인을 하면 nginx_1이 앞에 붙어서 로그가 찍힐 것이다.

[code lang=text]
$ curl https://USERNAME:PASSWORD@domain:5443/v2/
[/code]

클라이언트 머신에서 docker registry 접근하기

다른 머신에서 도커 레지스트리에 접속하려면 루트인증서 파일을 해당 서버에 등록해주어야 한다.

[code lang=text]
$sudo cat /docker-registry/nginx/rootCA.crt
[/code]

위의 명령어를 실행하면 아래와 같은 형식의 암호화 문자열이 나올것이다.

[code lang=text]
—–BEGIN CERTIFICATE—–
MIIDfTCCAmWgAwIBAgIJAKJf+hKlYzotMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV
…. 중략…
ShmxzUJQAcQwdI2Wrivv9QG4kcRPw73lrMTNU8FfYnfQ
—–END CERTIFICATE—–
[/code]

이걸 docker registry서버의 머신에서 해주었던 것 처럼 등록해주어야 한다.

우분투의 경우

[code lang=text]
$ sudo mkdir /usr/local/share/ca-certificates/docker-dev-cert
$ sudo cp rootCA.crt /usr/local/share/ca-certificates/docker-dev-cert/
$ sudo update-ca-certificates
[/code]

Centos의 경우

[code lang=text]
$ sudo cp rootCA.crt /etc/pki/ca-trust/source/anchors/
$ sudo update-ca-trust enable
$ sudo update-ca-trust extract
[/code]

루트 인증서를 추가후 도커를 재시작 해야한다.

[code lang=text]
$ sudo service docker restart
[/code]

인증을 사용하는 경우 docker login을 사용해야 하는데, 사용법은 아래와 같다.

[code lang=text]
$ docker login https://<domain>
[/code]

UsernamePassword Email을 물어보는데, Email은 그냥 아무것도 입력 하지 않으면 된다.

성공인 경우 아래와 같은 메세지가 뜬다.

[code lang=text]
Login Succeeded
[/code]

이미지를 올려보자.

가장 쉬운 hello-world로 해보자.

[code lang=text]
$ sudo docker run hello-world
$ sudo docker tag hello-world deploy.gyus.me:5443/hello-world
$ sudo docker push deploy.gyus.me:5443/hello-world
[/code]

위와 같은 스크립트를 실행시키면 성공시에 아래와 같은 메세지들이 나온다.

[code lang=text]
The push refers to a repository [deploy.gyus.me:5443/hello-world]
5f70bf18a086: Pushed
b652ec3a27e7: Pushed
latest: digest: sha256:fea8895f450959fa676bcc1df0611ea93823a735a01205fd8622846041d0c7cf size: 708
[/code]

다른 머신에서 이미지를 땡겨와보자.

땡겨오는 법은 그냥 push 대신 pull을 쓰면 된다.
물론 인증서 작업은 완료된 상태이어야 한다.

[code lang=text]
$ docker pull deploy.gyus.me:5443/hello-world
[/code]

[code lang=text]
$ docker run deploy.gyus.me:5443/hello-world
[/code]

잘 실행된다!

[code lang=text]
Hello from Docker.
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker Hub account:
https://hub.docker.com

For more examples and ideas, visit:
https://docs.docker.com/userguide/
[/code]

느낀점

처음에는 좀 어렵다 귀찮다라는 느낌이 강했는데,
차근 차근 설치해보니, 크게 어려운 점은 없었다.

https로 통신을 해야하니 자가인증기관과 자가인증서를 만들어 주는 부분을
처음 해보는거라서 좀 이상하긴 했지만, 이 부분은 나중에 또 공부를 해야되는 부분으로
남겨두기로 한다.

참고 링크

아래의 링크를 대부분 참고했고, 따라하면서 내용을 조금씩 추가하거나 생략 하거나 했다.
디지탈 오션

추가 (인증서 인증 실패 관련)

자가인증서를 사용하게 되면, 아래와 같은 에러가 나는 경우가 있다.

[code lang=text]
tls: oversized record received with length 20527
[/code]

이런경우 /etc/docker/certs.d/domain:5443 의 경로에
위에서 만든 rootCA.crt 파일을 복사한후 도커를 재시작 해주면 된다.
자세한 것은 아래의 링크를 참고하자.

https://docs.docker.com/registry/insecure/

[/markdown]

Chef Server 12으로 인프라 자동화하기

Chef Server 12를 사용해보기

 

2년전에 chef-server로 게임서버들을 잘 구성해서 사용했었다.
knife-solo로 하면 10대 정도까지는 그냥 관리가 가능하긴 한데,
role이나 databag같은 메타데이터를 다루게 되면,
그때부터는 chef-server를 사용하는 것도 좋은 것 같다.

2년 전에도 쉐프 서버 구축할 때 이리저리 고생을 했던 기억이 나는데,
정리를 제대로 안해놨더니, 했던 삽질을 다시하면서 나의 멍청함을 뇌로 되새기고 있다.

아무튼 다시는 그런일이 벌어지지 않게 하기 위해서,
좀 정성들여서 정리를 하는중이고, 특히 에러가 나는 포인트를 계속해서 정리중이다.

chef서버와 client는 virtual box를 사용해서 2대를 실행했고,
workstation은 mac에 chefdk를 설치해서 돌리고 있다.

사실 chef server 이외에는 chef client & knife가 깔리면 되는거라서, 뭘 어떻게 하든 크게 관계는 없을 것 같다.

기본지식

  • chef-server-core만 인스톨한 후에는 chef-server-ctl커맨드로 reconfigure을 실행해주어야한다. 설정파일이 변경되면 reconfigure를 실행해야한다.
  • chef-server-core관련 파일은 /etc/opscode에 있다.
  • 로그는 /var/log/opscode에 있다
  • chef서버의 로그를 실시간으로 보려면 chef-server-ctl tail 명령어를 사용하면 된다.

chef server 12에서는 여러가지 기능이 추가된 것 같긴한데, 기본만 설치하자.
– chef-server-core
– manage ui

chef-server-core 설치

아래 링크에서 chef-server-core_12.x.x-x_amd64.deb를 받는다. (버전은 그때 그때 다르다. RHEL은 패스)
2016년 10월의 최신버전은 12.9.1이다.

우분투 16.04
https://packages.chef.io/stable/ubuntu/16.04/chef-server-core_12.9.1-1_amd64.deb

우분투 14.04
https://packages.chef.io/stable/ubuntu/14.04/chef-server-core_12.9.1-1_amd64.deb

우분투 12.04
https://packages.chef.io/stable/ubuntu/12.04/chef-server-core_12.9.1-1_amd64.deb

아래 명령어를 실행

[code lang=text]
$ sudo dpkg -i chef-server-core_12.x.x-x_amd64.deb
$ sudo chef-server-ctl reconfigure
[/code]

reconfigure 실행시의 에러 처리

reconfigure를 실행시에 아래와 같은 에러가 날 수 있는데, 이는 nginx cookbook의 이슈이다.

[code lang=text]
Chef::Exceptions::ValidationFailed
———————————-
common_name is required
[/code]

이를 해결하려면 /etc/hosts에 유효한 호스트명을 뭐라도 넣어줘야된다.
에는 hostname 이라는 명령어를 치면 나오는 녀석으로 변경해야한다.

sudo echo "127.0.0.1 localhost localhost.localdomain <hostname>" | sudo tee -a /etc/hosts

이게 오픈소스가 되서그런지 버그가 은근 있는데, reconfigure 시에 아래와 같은 에러가 나올 수도 있다.

[code lang=text]
ubuntu@chef-server:~$ sudo chef-server-ctl reconfigure
Starting Chef Client, version 12.14.98
resolving cookbooks for run list: [“private-chef::default”]
Synchronizing Cookbooks:
– enterprise (0.10.1)
– yum (3.13.0)
– runit (1.6.0)
– packagecloud (0.2.5)
– apt (2.9.2)
– openssl (6.0.0)
– private-chef (0.1.0)
Installing Cookbook Gems:
Compiling Cookbooks…
[2016-10-21T03:00:43+00:00] WARN: Chef::Provider::AptRepository already exists! Cannot create deprecation class for LWRP provider apt_repository from cookbook apt
[2016-10-21T03:00:43+00:00] WARN: AptRepository already exists! Deprecation class overwrites Custom resource apt_repository from cookbook apt
[2016-10-21T03:00:43+00:00] WARN: Chef::Provider::YumRepository already exists! Cannot create deprecation class for LWRP provider yum_repository from cookbook yum
[2016-10-21T03:00:43+00:00] WARN: YumRepository already exists! Deprecation class overwrites Custom resource yum_repository from cookbook yum
Recipe: private-chef::default
* directory[/etc/opscode] action create (up to date)
* directory[/etc/opscode/logrotate.d] action create (up to date)
[2016-10-21T03:00:43+00:00] FATAL:

———————————————————————–
BOOT007: The secrets file (/etc/opscode/private-chef-secrets.json) is present
but the file /etc/opscode/pivotal.pem is missing.

Ensure that private-chef-secrets.json is copied into /etc/opscode from the
first Chef Server node that you brought online, then run
‘chef-server-ctl reconfigure’ again.
———————————————————————–
[/code]

이 경우에는 /etc/opscode/private-chef-secrets.json 파일을 삭제하고
다시 sudo chef-server-ctl reconfigure를 해주면 된다.

user/org를 생성

[code lang=text]
### user생성
chef-server-ctl user-create user_name first_name last_name email password –filename ADMIN_PEM_FILE_NAME

### org생성
chef-server-ctl org-create short_name full_organization_name –association_user user_name –filename ORG_VALIDATION_KEY_FILE_NAME
[/code]

/etc/opscode/admin.pem
/etc/opscode/orgname-validator.pem
등의 이름이 뭘하는 파일인지 알아보기 쉽다.

chef manage ui 설치

이번 버전부터 ui가 엄청 이뿌게 변했다.
소스를 대충 보니 bootstrap을 사용한것 같은데, 개발자스럽긴하지만, 이쁜 UI이다.

[code lang=text]
$ sudo chef-server-ctl install opscode-manage
$ sudo opscode-manage-ctl reconfigure
[/code]

12.9 버전에서 opscode-manage가 chef-manage로 변경되었다. 최신버전을 쓰는 사람은 chef-manage로 설치하자

[code lang=text]
$ sudo chef-server-ctl install chef-manage
$ sudo chef-server-ctl reconfigure
$ sudo chef-manage-ctl reconfigure
[/code]

workstation 구축

chef를 설치

[code lang=text]
$ sudo curl -L https://www.opscode.com/chef/install.sh | sudo bash
[/code]

chefdk(chef development kit) 라는 녀석을 설치해도 된다.

chefdk에는 chef, Test Kitchen, ChefSpec, Foodcritic, Chef Client, Knife, Ohai, Chef Zero 등이 함께 들어 있어서 쉐프 레시피개발 및 테스트에 좋다.

knife 설정

[code lang=text]
$ knife configure
Where should I put the config file? [/home/USER/.chef/knife.rb]
Please enter the chef server URL: [https://xxx:443] https://chef-server/organizations/org
Please enter an existing username or clientname for the API: [user] <USER>
Please enter the validation clientname: [chef-validator] <ORG>-validator
Please enter the location of the validation key: [/etc/chef-server/chef-validator.pem] /Users/wapj/chef/.chef/<ORG>-validator.pem
Please enter the path to a chef repository (or leave blank): <CHEF-REPO-PATH>
[/code]

위와 같이 하면 .chef 디렉토리 아래에 knife.rb 파일이 생긴다.
해당 파일은 아래와 같이 생겼다.

[code lang=text]
### knife.rb
log_level :info
log_location STDOUT
node_name ‘wapj’
client_key ‘/Users/wapj/chef/.chef/admin.pem’
validation_client_name ‘company-validator’
validation_key ‘/Users/wapj/chef/.chef/org-validator.pem’
chef_server_url ‘https://wapj-VirtualBox/organizations/wapj’
syntax_check_cache_path ‘/Users/wapj/chef/.chef/syntax_check_cache’
cookbook_path [“/Users/wapj/chef/site-cookbooks”]
[/code]

admin.pem파일과 org-validator.pem 파일은 쉐프 서버에서 scp 명령어 등으로 잘 가져와서 해당디렉토리에 복사하자.

chef서버와 잘 통신되는지 확인

아래와 같은 명령어로 확인이 가능하다.

[code lang=text]
$ knife ssl check
[/code]

trouble shooting

knife.rb의 chef_server_url설정에 아이피를 넣은 경우

아래와 같은 에러가 난다.

[code lang=text]
Connecting to host 192.168.10.111:443
ERROR: The SSL cert is signed by a trusted authority but is not valid for the given hostname
ERROR: You are attempting to connect to: ‘192.168.10.111’
ERROR: The server’s certificate belongs to ‘wapj-VirtualBox’

TO FIX THIS ERROR:

The solution for this issue depends on your networking configuration. If you
are able to connect to this server using the hostname wapj-VirtualBox
instead of 192.168.10.111, then you can resolve this issue by updating chef_server_url
in your configuration file.

If you are not able to connect to the server using the hostname wapj-VirtualBox
you will have to update the certificate on the server to use the correct hostname.
[/code]

이 경우 /etc/hosts에 server의 domain혹은 QFDN을 아래와 같은 식으로 넣어주어야 한다.

[code lang=text]
# hosts
192.168.10.111 wapj-VirtualBox
[/code]

chef server에서 받은 인증서가 localhost로 되어 있을 때

로컬에서만 테스트하다가 실제로 서버를 올리면 이런 일이 생기곤 한다.
workstation 에서 아래의 명령어를 실행했는데

[code lang=text]
$ knife ssl check
[/code]

아래와 같은 에러가 나는경우

[code lang=text]
Connecting to host chef.gyus.me:443
ERROR: The SSL cert is signed by a trusted authority but is not valid for the given hostname
ERROR: You are attempting to connect to: ‘chef.gyus.me’
ERROR: The server’s certificate belongs to ‘localhost’

TO FIX THIS ERROR:

The solution for this issue depends on your networking configuration. If you
are able to connect to this server using the hostname localhost
instead of chef.gyus.me, then you can resolve this issue by updating chef_server_url
in your configuration file.

If you are not able to connect to the server using the hostname localhost
you will have to update the certificate on the server to use the correct hostname.
[/code]

이 경우는 chef server의 /etc/opscode/chef-server.rb 파일에 설정을 추가해주어야한다.
chef server의 도메인이 chef.gyus.me라고 한다면, 아래와 같이 수정해 주면 된다.

[code lang=text]
### /etc/opscode/chef-server.rb
api_fqdn “chef.gyus.me”
[/code]

설정 파일이 수정되었으므로 서버에서 chef-server-ctl reconfigure를 한번더 실행해준다.

그리고 workstation으로 돌아와서 인증서를 받아오는 작업을 다시 해주자.

[code lang=text]
$ knife ssl fetch
$ knife ssl check
[/code]

아래와 같이 나오면 성공이다.

[code lang=text]
Connecting to host chef.gyus.me:443
Successfully verified certificates from `chef.gyus.me’
[/code]

아래와 같은 에러가 날 때

[code lang=text]
$knife status
ERROR: The object you are looking for could not be found
Response: <!DOCTYPE html>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″ />
<meta http-equiv=”X-UA-Compatible” content=”IE=EmulateIE7″ />
<title>Chef – 404 Not Found</title>
<link media=”all” rel=”stylesheet” type=”text/css” href=”/css/all.css” />
<!–[if lt IE 7]><link rel=”stylesheet” type=”text/css” href=”/css/lt7.css” /><![endif]–>
</head>
<body>
<div class=”header-block”>
<div id=”header”>
<strong class=”logo”><a href=”http://www.getchef.com”>Chef</a></strong>
</div>
</div>
<div id=”wrapper”>
<div id=”main”>
<div class=”mybox”>
<div id=”content”>
<h1>404 – Not Found</h1>
<p>Sorry, I can’t find what you are looking for.</p>
</div>
</div>
</div>
</div>
<div class=”footer-block”>
<div id=”footer”>
<div class=”mybox”>
</div>
<div class=”footer-bottom”>
<span>© 2010 – 2014 Chef Software, Inc. All Rights Reserved</span>
</div>
</div>
</div>
</body>
</html>
[/code]

knife.rb 설정의 chef_server_url에 정말로 serverURL만 넣은 경우에 나는 에러이다.

아래와 같은 식으로 되어있다면,

[code lang=text]
chef_server_url https://wapj-VirtualBox/
[/code]

뒤에 organizations/<ORGANIGATION>을 붙여주자.

[code lang=text]
chef_server_url https://wapj-VirtualBox/organizations/wapj
[/code]

쿡북 만들기

아래 명령어로 hello라는 쿡북을 site-cookbooks에 만들었다.
레시피

[code lang=text]
$ knife cookbook create hello -o site-cookbooks
[/code]

간단히 아래 파일을 변경하자.

[code lang=text]
site-cookbooks/hello/recipes/defau1t.rb
[/code]

[code lang=text]
### default.rb
log “Hello , Chef!”
[/code]

쿡북을 서버로 업로드하기

아래의 커맨드를 사용하면 모든 쿡북이 서버로 업로드 된다.

[code lang=text]
$ knife cookbook upload -a
[/code]

하나의 쿡북만 업로드 하고 싶은 경우는 아래의 커맨드를 사용하자.

[code lang=text]
$ knife cookbook upload <쿡북이름> -o <해당쿡북의 폴더가 있는 경로>
$ knife cookbook upload hello -o ./site-cookbooks/
[/code]

쿡북 업로드시에 SSL 에러가 나는 경우

[code lang=text]
WARN: Found a directory hello in the cookbook path, but it contains no cookbook files. skipping.
ERROR: SSL Validation failure connecting to host: 192.168.10.111 – hostname “192.168.10.111” does not match the server certificate
[/code]

자가 인증서로 검증하기 때문에 나는 당연한 에러(?!) 라서, 검증하지 말라고 하면 된다..;;

아래 한줄을 knife.rb에 추가해주자

[code lang=text]
ssl_verify_mode :verify_none
[/code]

chef-client

chef 설치

  • 서버와의 인증에는 비밀키를 사용한다.
  • chef server는 client별로 비밀키를 발행하고, 공개키를 chef-server에 등록한다.
  • client별로 비밀키를 발행해야 하기 때문에, 미리 준비된 validator-key를 사용한다.
  • client는 처음에는 validator-key로 통신해서 chef server에 client로 등록한후 이후의 통신에 필요한 비밀키가 발행된다.
  • workstation에서 삽질했던것 처럼 hosts에 chef server의 ip를 등록해야한다.

knife bootstrap으로 client 등록하기

전제 조건
1. chef server에서 client서버로 비밀번호 없이 root계정으로 로그인을 할 수 있어야한다.
2. bootstrap 을 실행할 서버의 /etc/hosts에 chef server의 ip를 등록한다.

1번 조건은 root의 홈디렉토리에 chef server의 퍼블릭키를 authorized_keys파일에 등록하면 된다.
2번은 위의 workstation 설정시에 보았던, /etc/hostschef server의 ip를 등록하면 된다.

chef server가 만약에 해킹당하게 되면, 모든 서버에 접속 할 수 있으므로 이 방법을 사용할 때에는,
chef server의 보안에 특히 주의 해야한다.
나 같은 경우는 chef server를 사용할 때만 켰다가 끄는 방식으로 사용했다.

위의 조건이 만족되면, 아래의 커맨드로 바로 노드를 등록할 수 있다.

[code lang=text]
$ knife bootstrap <client server ip> -x <user> -N <node name>
[/code]

성공하게 되면 아래와 같은 메세지가 나오게 된다.

[code lang=text]
### workstation의 chef repo 디렉토리에서 실행하면 된다.

$knife bootstrap 192.168.10.101 -x root -N node1
Doing old-style registration with the validation key at /Users/wapj/chef/my-chef/.chef/company-validator.pem…
Delete your validation key in order to use your user credentials instead

Connecting to 192.168.10.101
192.168.10.101 —–> Existing Chef installation detected
192.168.10.101 Starting the first Chef Client run…
192.168.10.101 Starting Chef Client, version 12.7.2
192.168.10.101 Creating a new client identity for node1 using the validator key.
192.168.10.101 resolving cookbooks for run list: []
192.168.10.101 Synchronizing Cookbooks:
192.168.10.101 Compiling Cookbooks…
192.168.10.101 [2016-02-16T21:02:32+09:00] WARN: Node node1 has an empty run list.
192.168.10.101 Converging 0 resources
192.168.10.101
192.168.10.101 Running handlers:
192.168.10.101 Running handlers complete
192.168.10.101 Chef Client finished, 0/0 resources updated in 02 seconds
[/code]

node가 등록되었는지 확인

[code lang=text]
### workstation의 chef repo에서 실행
$ knife node list
[/code]

잘 등록되었다면 아래와 같이 나올 것이다.

[code lang=text]
node1
[/code]

node에 레시피 등록 하기

노드는 등록되었지만, 레시피가 아무것도 없으므로 실행해봐야 아무것도 안나온다.
레시피를 등록해보자.

해당 작업은 chef server의 WEB UI에서도 가능하므로, 웹사이트에 들어가서 해도 된다.

[code lang=text]
### workstation의 chef repo에서 실행
$ knife node run_list add <노드명> ‘recipe[레시피명]’
$ knife node run_list add node1 ‘recipe[hello]’
[/code]

node에서 레시피 실행하기

레시피의 실행은 각 노드 서버에 들어가서 $ sudo chef-client 를 실행해주면 되지만,
귀찮게 일일이 들어가면 굳이 서버를 설치한 이유가 없으니 서버에서 실행할 수 있는 방법을 찾아보자.

바로 knife search node를 이용하는 방법인데,

아래의 명령어를 실행하면, 조건을 줘서 chef client를 찾을 수 있다.

[code lang=text]
$ knife search node “fqdn:*”
[/code]

그리고 조건은 knife ssh에도 동일하게 사용가능 한데, 조건과 조합하여 여러서버에 커맨드를 날릴수있다.
예를 들어 아래와 같이 실행하게 되면, 모든 서버에 echo "hello" 명령을 날리게 된다.

[code lang=text]
$ knife ssh “fqdn:*” ‘echo “hello”‘
[/code]

이를 응용하여 아래와 같이 실행하게 되면, 모든 클라이언트에 sudo chef-client를 실행 할 수 있게 된다.

[code lang=text]
$ knife ssh “fqdn:*” “sudo chef-client”
[/code]

hosts에 정보가 없는 경우

아래와 같은 에러가 난다.

[code lang=text]
WARNING: Failed to connect to node1 — SocketError: getaddrinfo: nodename nor servname provided, or not known
[/code]

이 경우는 workstation의 /etc/hosts에 node1의 ip를 등록해야한다.

[code lang=text]
### workstaions의 /etc/hosts
192.168.10.101 node1
[/code]

다시 실행해보면~

[code lang=text]
$ knife ssh “fqdn:*” “sudo chef-client”
[/code]

아직도 에러가 난다;;;

[code lang=text]
node1
node1 Starting Chef Client, version 12.7.2
node1 resolving cookbooks for run list: [“hello”]
node1 Synchronizing Cookbooks:
node1 [2016-02-16T21:45:15+09:00] ERROR: SSL Validation failure connecting to host: wapj-virtualbox – hostname “wapj-virtualbox” does not match the server certificate
node1 [2016-02-16T21:45:15+09:00] ERROR: SSL Validation failure connecting to host: wapj-virtualbox – hostname “wapj-virtualbox” does not match the server certificate
node1 [2016-02-16T21:45:15+09:00] ERROR: SSL Validation failure connecting to host: wapj-virtualbox – hostname “wapj-virtualbox” does not match the server certificate
node1 ================================================================================
node1 Error Syncing Cookbooks:
node1 ================================================================================
node1
node1 Unexpected Error:
node1 —————–
node1 OpenSSL::SSL::SSLError: SSL Error connecting to https://wapj-virtualbox/bookshelf/organization-7479a4a25da1226f99983095c54052ba/checksum-40dcaffc3a2cdc8968a18f61672dab63?AWSAccessKeyId=49472f43a12b78c05ea083a9e82ac20de4ca4463&Expires=1455655515&Signature=ftp5IYQKkyvjyXYPG%2BlPltfh/18%3D – hostname “wapj-virtualbox” does not match the server certificate
node1 Running handlers complete
node1 [2016-02-16T21:45:40+09:00] ERROR: Exception handlers complete
node1 Chef Client failed. 0 resources updated in 27 seconds
node1 [2016-02-16T21:45:40+09:00] FATAL: Stacktrace dumped to /var/chef/cache/chef-stacktrace.out
node1 [2016-02-16T21:45:40+09:00] ERROR: SSL Validation failure connecting to host: wapj-virtualbox – hostname “wapj-virtualbox” does not match the server certificate
node1 [2016-02-16T21:45:40+09:00] FATAL: Please provide the contents of the stacktrace.out file if you file a bug report
node1 [2016-02-16T21:45:40+09:00] ERROR: SSL Error connecting to https://wapj-virtualbox/bookshelf/organization-7479a4a25da1226f99983095c54052ba/checksum-40dcaffc3a2cdc8968a18f61672dab63?AWSAccessKeyId=49472f43a12b78c05ea083a9e82ac20de4ca4463&Expires=1455655515&Signature=ftp5IYQKkyvjyXYPG%2BlPltfh/18%3D – hostname “wapj-virtualbox” does not match the server certificate
node1 [2016-02-16T21:45:40+09:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)
[/code]

에러는 인증서가 매치가 안된다는 말인데, 자가 인증서이기 때문에 안되는 문제이다.

/etc/chef/client.rbssl_verify_mode :verify_none을 또 추가해주자.

아래와 같은 형태로 client.rb가 되어 있으면 된다.

[code lang=text]
### node1 서버 client.rb
log_location STDOUT
chef_server_url “https://chef-server/organizations/company”
validation_client_name “company-validator”
node_name “node1”
trusted_certs_dir “/etc/chef/trusted_certs”
ssl_verify_mode :verify_none
[/code]

다시 실행해보자

[code lang=text]
$ knife ssh “fqdn:*” “sudo chef-client”
[/code]

아래와 같이 실행된다.

[code lang=text]
node1
node1 Starting Chef Client, version 12.7.2
node1 resolving cookbooks for run list: [“hello”]
node1 Synchronizing Cookbooks:
node1 – hello (0.1.0)
node1 Compiling Cookbooks…
node1 Converging 1 resources
node1 Recipe: hello::default
node1 * log[Hello Chef!] action write
node1
node1
node1 Running handlers:
node1 Running handlers complete
node1 Chef Client finished, 1/1 resources updated in 01 seconds
[/code]

개발PC에서 cookbook을 만들어서 올리고, chef의 API를 사용해서 실행하는 것을 보여주기위해
workstation이라는 것을 예로 들었지만, workstation을 굳이 만들지 않아도 되고, chef serverchef를 설치해서 하는 것이 더 간단한 방법일 수도 있다.
개념을 파악하고 구조를 파악해서, 각자의 환경에 맞게 잘 사용하면 될 것 같다.

좀 길었지만, chef server 설치중에 내가 만난 문제들은 다 정리가 된것 같다.
몇가지 수동으로 했던 부분들을 자동화하게 되면, 훌륭한 인프라 툴이 될 것이다.

TIPS

여기서 부터는 꼭 안봐도 되고, 약간씩 도움이 되는 팁을 정리하려고 한다.
알면 좋고 모르면, 그냥 손이 수고하면 되는(?) 그런 부분이다. 귀찮으면 과감히 패스~

knife.rb 설정에 변수 사용하기

팁이라고 하기도 뭐한 팁인데,
쉐프의 소스가 git 같은 소스 관리 프로그램으로 관리되는 경우,
키의 경로를 절대 경로로 잡아주게 되면,
다른 pc나 인스턴스에서 쉐프 소스를 받게 되면, 매번 수정해야 하는 번거로움이 생긴다.
이 때에 각 키의 경로를 그냥 knife.rb와 같은 경로로 변수를 사용하는 팁이 되시겠다.

knife.rb 파일은 말 그대로 루비 스크립트 이므로 루비의 문법을 사용하면 되는데,
File.dirname(__FILE__) 이라는 녀석이랑 erb의 문법을 사용할 것이다.

[code lang=text]
### knife.rb
urrent_dir = File.dirname(__FILE__)
log_level :info
log_location STDOUT
node_name “company”
client_key “#{current_dir}/admin.pem”
validation_client_name “company-validator”
validation_key “#{current_dir}/gyus-validator.pem”
chef_server_url “https://deploy.gyus.me/organizations/gyus”
syntax_check_cache_path “#{current_dir}/syntax_check_cache”
ssl_verify_mode :verify_mode
[/code]

위의 파일에서 중요한 것은 홑따옴표 ' 가 아니라 쌍따옴표 "를 써야 변수를 루비가 인식하고 변환해준다는 점이다.

업데이트 (2016.10.21)

자가 인증서가 아닌 comodo등의 기관에서 발급받은 인증서를 적용하고 싶은경우.

/var/opt/opscode/nginx/ca 아래에 있는 chef-server.crt, chef-server.key 를 자신이 가지고 있는 인증서로 바꿔치기 하면된다.

파일명을 같게한다음(확장자는 맞춰줘야함) 덮어쓰자.

그 후 설정을 변경하기위해 sudo chef-server-ctl reconfigure 를 실행하고 sudo chef-server-ctl restart를 실행해주자.

이렇게 하면 끝난다. 파일명을 다르게 적용하고 싶다면, /var/opt/opscode/nginx/etc/chef_https_lb.conf 안에 있는 설정을 변경해야한다.

아래와 같은 설정이 있는데, 파일명도 바꿔주면 된다.

[code lang=text]
ssl_certificate /var/opt/opscode/nginx/ca/chef-server.crt;
ssl_certificate_key /var/opt/opscode/nginx/ca/chef-server.key;
[/code]

어려울 줄 알았는데, 생각보다 너무 쉬웠음.

docker 명령어들

[markdown]

# docker 명령어들

도커로 apache php mysql환경을 구성중이다.
apache와 php를 하나의 환경으로 묶기로 했고, mysql은 따로 하나더 묶을 예정이다.

이 글은 위의 환경을 구축중에 공부하고 있는 것들을 정리하는 글인데, 위의 환경 구축이 완료되면
관련해서 글을 하나 더 쓸 수 있을 것 같다.

도커가 간단하네 심플하네 빠르네, 이런 것들로 홍보를 하고 있지만,
알아야 될 것 들이 생각보다 많다.
명령어를 도저히 다 외울 수가 없어서 그냥 정리하기로 했다.

도커를 공부하면서 좋은 것 하나는 bash와 자동화에 대한 지식이 뽀너스로 늘어나는 것이 좋은 것 같다.
물론 알아야되는게 많아서 짜증나는 점이기도 하다.

오픈소스 홈페이지들에 올라가 있는 정보는 내가 보기에는 너무 상세하고 스펙적인게 대부분이기 때문에,
그냥 이렇게 케이스별로 정리를 하는게 나에게는 도움이 많이 되었던것 같다.

찾아보면 대부분 나오는 것들이겠지만,
도커를 하면서 조금이라도 삽질을 줄이려면, 자신만의 커맨드라인 리스트 같은게 있으면
개발할 때 좋을 것 같다.

아직 공부중이기 때문에 해당 포스트는 추후에 좀 더 내용이 추가될 가능성이 있다.

### 도커설치(우분투)

“`
$ sudo apt-get update
$ sudo apt-get install docker.io
“`

### 도커설치(Centos)

“`
$ sudo yum update
$ curl -fsSL https://get.docker.com/ | sh
$ sudo service docker start
“`

### 도커설치 윈도우, OSX

### docker 실행시 관리자 권한으로 실행되게 하기
매번 실행할때마다 sudo 넣는게 귀찮은 사람은 실행하면 좋다. (ubuntu에서는 잘되는데 cent에서는 잘 안됨!)

“`
$ sudo usermod -aG docker ${USER}
$ sudo service docker restart
“`

아래와 같은 에러가 나는 경우. 재부팅을 해주면 된다.
“`
FATA[0000] Post http:///var/run/docker.sock/v1.18/images/create?fromImage=registry%3Alatest: dial unix /var/run/docker.sock: permission denied. Are you trying to connect to a TLS-enabled daemon without TLS?
“`

### docker 프로세스 모두 죽이기

“`
$ docker stop $(docker ps -qa)
“`

### docker 컨테이너 모두 삭제하기

가상머신에서 도커이미지를 만들게 되면, 용량이 부족하다고 가끔 뜨는데 그때 필요하다.

“`
$ docker rm $(docker ps -qa)
“`

### docker 이미지 모두 삭제하기
요것도 가상머신에서 용량이 부족하다고 뜰때 필요하다.
“`
$ docker rmi $(docker images -qa)
“`

### 도커 컨테이너 데몬모드로 시작시키기

“`
docker run -d image명
“`

### 도커 컨테이너에 배쉬연결

“`
docker run -it image명
“`

### 기존 ENTRYPOINT를 무시하고 entrypoint설정하기

“`
docker run –entrypoint=”/bin/bash”
“`

### 컨테이너끼리 연결하기

“`
docker run –link=”db:db” image명
“`

### 데몬모드로 띄우면서 포트, 볼륨, 환경설정까지 해보기

“`
docker run -d -p 8080:80 -v /home/user/www:/var/www -e PHP_ERROR_REPORTING=’E_ALL & ~E_STRICT’
“`
[/markdown]

Docker machine에 대하여

Mac에서 도커를 쓸려면 docker-machine 이라는 녀석을 무조건 써야되는데,
그냥 도커를 돌릴 수 있는 가상머신이라고 생각하니 마음이 편해졌다.
더 알고 싶으면, 밑에 글도 읽어보자.

아래 그림에서 Docker가 돌아가는 Linux VM이 docker-machine이라는 것이드아~

docker-machine
[gfm]
### Docker machine

– 도커 호스트가 설치된 VM이라고 생각하면 됨.
– VM이므로 개발PC, 클라우드, 데이터센터등 아무데나 설치가능
– 클라우드 서비스의 경우는 해당 클라우드의 서비스에 특성에 맞춰주는 driver가 존재함.
– 로컬에 설치하는 경우는 virtualbox, vmware를 지원함.
– 보통 Dooker toolbox로 설치하면 들어있음
– 명령어는 docker-machine 임
– 실제로 docker-machine을 만들고 start를 하면 mac에서는 virtualbox의 VM이 시작됨.

### Docker machine 생성하기
“`
$ docker-machine create –driver virtualbox dev
“`
create 명령으로 생성하는데, driver는 virtualbox가 기본값이고, aws, azure, digital ocean등등이 있다. 왜 이름이 드라이버인지는 아직 모르겠다.

“`
Running pre-create checks…
Creating machine…
(dev) Copying /Users/wapj/.docker/machine/cache/boot2docker.iso to /Users/wapj/.docker/machine/machines/dev/boot2docker.iso…
(dev) Creating VirtualBox VM…
(dev) Creating SSH key…
(dev) Starting the VM…
(dev) Waiting for an IP…
Waiting for machine to be running, this may take a few minutes…
Machine is running, waiting for SSH to be available…
Detecting operating system of created instance…
Detecting the provisioner…
Provisioning with boot2docker…
Copying certs to the local machine directory…
Copying certs to the remote machine…
Setting Docker configuration on the remote daemon…
Checking connection to Docker…
Docker is up and running!
To see how to connect Docker to this machine, run: docker-machine env dev
“`

실행결과를 살펴보면,

– boot2docker.iso를 카피한다음에 VM을 만들고
– ssh키를 만들고
– VM을 기동시키고, 네트웤이 올라오는걸 기다린다.
– 올라오면 boot2docker를 프로비저닝하고
– 인증키를 local -> remote 로 복사하고
– remote(여기서는 VM)에 있는 Docker daemon에 필요한 도커 설정을한다.
– 그뒤에 도커에 잘 접속되는지 체크한다.

docker-machine에 접속하기위한 정보를 보기위해서는

“`
$ docker-machine env dev
“`

를 실행하면 되고, 그 결과는 아래와 같다.

“`
export DOCKER_TLS_VERIFY=”1″
export DOCKER_HOST=”tcp://192.168.99.101:2376″
export DOCKER_CERT_PATH=”/Users/wapj/.docker/machine/machines/dev”
export DOCKER_MACHINE_NAME=”dev”
# Run this command to configure your shell:
# eval $(docker-machine env dev)
“`

도커 호스트의 ip와 포트 인증서의 위치, 도커 머신의 이름등이 나온다.

생성한 도커 머신을 확인하기
“`
$ docker-machine ls
“`

위의 명령어로 확인 하면 된다.

그리고 도커 머신과 정보를 주고받으려면 (이걸 공홈에서는 대화한다고 표현) 해당 도커머신에 대한 환경설정을 해야한다.

“`
$ eval “$(docker-machine env dev)”
$ docker ps
“`

해당 설정을 하지 않으면 도커 머신에 접속할 수 없다는 에러가 뜨게 된다.
Cannot connect to the Docker daemon. Is the docker daemon running on this host?

### 도커 머신에 이미지 실행하기

생성된 도커 머신에 이미지를 다운받고 실행해보자.

“`
$ docker run busybox echo hello world
Unable to find image ‘busybox:latest’ locally
latest: Pulling from library/busybox
583635769552: Pull complete
b175bcb79023: Pull complete
Digest: sha256:c1bc9b4bffe665bf014a305cc6cf3bca0e6effeb69d681d7a208ce741dad58e0
Status: Downloaded newer image for busybox:latest
hello world
“`

로컬에는 해당 이미지가 없기 때문에 docker hub에서 받아오게 (pull)되고, 해당 이미지를 실행한다.
도커머신의 ip를 알기 위해서는 아래와 같은 명령을 실행하면 되는데,
요즘 세상에는 머신의 아이피가 바뀌는건 부지기수로 있는 일이니 머신 이름으로 아이피를 받아오는 방법을 잘 알아놓는 것이 좋을 것이다.

“`
$docker-machine ip dev
192.168.99.101
“`

그럼 이제 nginx이미지를 받아서 실행해보고 잘되는지 확인해보자.
“`
$ docker run -d -p 8000:80 nginx
“`

위의 이미지를 실행하면 nginx를 다운받게 되고, -p 옵션으로 도커 호스트의 8000번 포트로 들어가게 되면, 컨테이너의 80번 포트로 포워딩을 해주게된다.

curl로 확인해보자

“`
$ curl `docker-machine ip dev`:8000



Welcome to nginx!


Welcome to nginx!

If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.

For online documentation and support please refer to
nginx.org.
Commercial support is available at
nginx.com.

Thank you for using nginx.



“`

도커 머신은 말그대로 VM이므로 여러개 생성이 가능하다.

아래의 기본적인 명령어는 일단 숙지를 하고 다니자.

“`
$ docker-machine create –driver virtualbox [이름]
$ docker-machine ls #리스트 보기
$ docker-machine stop [이름] # 정지시키기
$ docker-machine start [이름] # 시작하기
$ docker-machine rm [이름] # 삭제하기
“`
[/gfm]

도커 아키텍쳐

서버의 소스를 배포하려고 하는 중인데, 내가 알고 있는 방식은 파일을 동기화 시키는 방식이었다. docker를 사용하면, 여러개의 파일을 동기화 안하고 그냥 이미지로 구워서 할 수 있는 것 같아서, 소스 배포할 때 편할것 같은 느낌이 들었기 때문에 조사해보기로 했다. 아무래도 잘 모르는 기술을 잘 파악하기 위해서는 아키텍쳐를 알면 좋기 때문에 시간을 내서 아래의 페이지를 요약 번역 해보았다.

https://docs.docker.com/engine/introduction/understanding-docker/

마음대로 번역한거라 혹시 틀린내용이 있을 수 있는데,  잘못된 것을 발견하게 되면 바로 수정하겠음.

Why docker?

공홈에 보면 주저리주저리 많은데 내가 이해한 바는 아래 3가지이다.

  • 어플리케이션의 빠른 배포
    • 리눅스이기만 하다면 서버환경에 의존성이 없음
    • 우분투에서 센트OS환경의 이미지를 돌릴 수 있음. 당연히 반대로도 가능
  • 배포와 스케일아웃을 쉽게 해줌
  • 머신의 성능을 최대치 까지 뽑아낼 수 있음. (고밀도 -> 고성능)

도커의 아키텍쳐는 어째 되나?

도커는 클라이언트-서버 아키텍쳐를 사용한다.
클라이언트와 서버는 호스트 머신에 설치된 Docker daemon으로 빌드와 실행, 배포등을 할 수 있도록 통신한다.

docker architecture

 

도커 데몬

도커 데몬은 호스트 머신에서 실행되고, 유저는 Docker client를 통해서 데몬과 상호작용한다.

도커 클라이언트

도커 데몬에 명령을 내릴 수 있도록 만든 도커의 주된 유저 인터페이스이다.

도커 인사이드

내부를 이해하려면 다음의 3가지 Docker Images, Docker registries, Docker containers 를 알아야한다.

Docker images

도커 이미지는 읽기전용 템플릿이다. 예를들면, 우분투에 아파치 웹서버를 설치한 것을 하나의 이미지라고 볼 수 있다.
도커 이미지는 도커 컨테이너를 만드는데 사용되고, 도커는 새로운 이미지를 빌드할 수 있는 심플한 방법을 제공한다. 혹은 다른 사람이 만든것을 다운받을 수도 있다. 이미지는 도커의 빌드 컴포넌트이다.

Docker registries

도커 레지스트리는 이미지들을 보관하는 곳이다. 이미지를 업로드/다운로드 할 수 있는 공개 저장소와 사설 저장소가 있다. 공개 저장소는 Docker hub에서 제공되고 있다.
도커 레지스트리는 도커의 배포 컴포넌트이다.

Docker containers

도커 컨테이너는 디렉토리와 비슷하다. 도커 컨테이너는 어플리케이션을 실행하기 위한 모든것을 가지고 있다. 각각의 컨테이너는 하나의 도커 이미지로 만들어 진다.
도커 컨테이너는 실행될수 있고, 시작 시킬 수 있고, 중지 시킬 수 있고, 이동 시킬 수 있고, 삭제 할 수 있다. 각각의 컨테이너는 고립되어 있어며 안전한 어플리케이션 플랫폼이다.
도커 컨테이너는 도커의 실행 컴포넌트이다.

그래서 도커는 어떻게 동작하나?

  1. 어플리케이션을 담기 위해 도커이미지를 빌드 할 수 있다.
  2. 도커 이미지로 부터 어플리케이션을 실행하기 위한 도커 컨테이너를 만들수 있다.
  3. 도커 이미지는 공개/사설 저장소에 공유할 수 있다.

도커 이미지는 어떻게 동작하나?

도커 이미지는 읽기전용 템플릿이고, 이것으로 부터 도커 컨테이너들을 작동시키는 것은 위에서 알게되었다.
각각의 이미지는 연속된 레이어들로 구성되어 있다. 도커는 union file systems를 사용해서 레이어들을 하나의 이미지로 합친다.
Union file systems는 분리되어 있는 여러 파일시스템을 투명하게 덮어서 하나의 파일 시스템 형태로 만들어준다.
여러개의 레이어가 있다는 것은 도커가 가벼운 이유 중 하나이다.
예를 들어서 어플리케이션을 새로운 버전으로 변경하기 위해 도커의 이미지를 다시 빌드한다고 해보자.
그런경우에 이미지 전체를 새로 빌드하는 것이 아니라, 새로 추가된 부분 또는 업데이트 된 부분만 변경하면 된다.
그리고 배포할 경우에도 변경된 부분만 배포하면 되기 때문에 도커 이미지는 더 빠르고 더 심플하다.
모든 이미지는 우분투나 페도라 같은 베이스 이미지로 부터 시작된다. 그리고 베이스 이미지는 보통 Docker hub에서 가져오게된다.
도커 이미지는 베이스 이미지에서 인스트럭션 셋을 작성하여 만들어지게 되는데, 각각의 인스트럭션은 이미지의 새로운 레이어를 생성하게 된다.
인스트럭션은 다음과 같은 액션들을 포함한다.

  • Run 커맨드
  • 파일또는 디렉토리의 추가
  • 환경변수 설정
  • 이미지에서 컨테이너를 런칭할때 어떤 프로세스가 실행되는지

도커 레지스트리는 어떻게 동작하나?

도커 레지스트리는 도커 이미지들을 위한 스토어이다. Docker hub 같은 퍼블릭 레지스트리를 사용하거나, 자체구축을 해서 사용해도 된다.
도커클라이언트로 업로드한 이미지들을 검색할 수 있고, 땡겨 받을 수도 있다.
Docker hub에도 사설 스토리지 서비스를 해주는데, 1개는 무료이지만 그 이상은 비용이 든다. 자세한건 https://hub.docker.com/plans 링크 참고

컨테이너는 어떻게 동작하나?

컨테이너는 운영체제, 유저가 추가한 파일들 그리고 메타데이터로 구성되어 있다. 이미 봤지만, 각각의 컨테이너는 이미지로 부터 빌드된다.
이미지는 컨테이너가 올라가려면, 어떤 프로세스를 실행해야하는지 그에 따른 이런 저런 설정파일을 컨테이너에 알려준다.
컨테이너가 이미지로 부터 실행이 되어서 올라갈 때에, 이미지의 최상단에 읽기-쓰기가 되는 레이어를 더하게 된다.

당신이 컨테이너를 실행할때 무슨일이 일어나나?

docker 바이너리를 사용하건 API를 사용하건 Docker 클라이언트는 Docker 데몬에게 컨테이너를 실행시키라고 요청을 보낸다.

$ docker run -i -t ubuntu /bin/bash

위의 커맨드를 한번 쪼개보자.
docker 커맨드로 도커 바이너리를 실행시키고, 새로운 컨테이너를 올리기위해 run 서브커맨드를 사용한다. 도커 클라이언트가 도커 데몬에게 요청을 보내기위한 최소한의 내용은 다음과 같다.

  • 컨테이너로 빌드할 도커 이미지가 무엇인가, 여기서는 ubuntu 베이스 이미지이다.
  • 컨테이너가 올라간 후 실행하고 싶은 커맨드는 무엇인가, 여기서는 /bin/bash 라는 명령어를 사용한다.

그러면 위의 명령어가 실행되면 어떤 일이 일어날까?

  • ubuntu 이미지를 밀어올린다.(Pull)
  • 새로운 컨테이너를 만든다.
  • 읽기-쓰기레이어에 파일 시스템을 할당하고 마운트한다.
  • 네트웍과 브릿지 인터페이스를 할당한다
  • 아이피를 세팅한다.
  • 당신이 지정한 프로세스를 실행한다.
  • 어플리케이션의 로그를 캡쳐한다.

도커를 지탱하는 기술(Underlying technology)

도커는 Go 언어를 사용하여 작성되었고, 몇가지 커널의 기능을 사용했다.

Namespaces

도커는 namespaces라는 워크스페이스(우리가 컨테이너라고 부르는) 격리화 기술의 이점을 차용했다. 컨테이너를 실행하게 되면, 도커는 해당컨테이너를 위한 namespaces의 셋을 생성한다.
Namespaces는 레이어의 격리를 제공한다 : 각각의 컨테이너는 그들 자신의 namespace를 가지게 되고 외부로는 접속할 수 없다.
리눅스에서 도커가 사용하는 몇가지 namespaces는 아래와 같다.

  • pid namespace : 프로세스 격리에 사용
  • net namespace : 네트워크인터페이스 관리에 사용
  • ipc namespace : IPC리소스 접근에 대한 관리에 사용 (IPC : InterProcess Communication)
  • mnt namespace : 마운트포인트의 관리에 사용
  • uts namespace : 커널격리와 버전 인식에 사용 (UTS : Unix Timesharing System)

Control groups

도커는 cgroup 또는 control groups라는 리눅스의 또다른 기술을 사용한다.
컨트롤 그룹은 도커가 CPU, 메모리, 디스크 같은 하드웨어리소스를 쉐어 할 수 있게 제어한다.

Union file systems

Union file systems또는 UnionFS라고도 하는 파일시스템은 레이어를 생성하는데 사용되고, 매우 가볍고 빠르다. 도커는 컨테이너를 위한 빌딩 블록을 제공하는데에 union file systems를 사용한다. 도커는 AUFS, btrfs, vfs, DeviceMapper 같은 Union file systems를 사용할 수 있다.

Container format

도커는 이러한 컴포넌트들을 조합하여 감싸는데 이걸 컨테이너 포맷이라고 한다. 디폴트 컨테이너 포맷은 libcontainer라고 부른다.
또한 도커는 LXC라는 전통적인 리눅스 컨테이너도 지원한다.
추후에는 BSD Jails또는 Solaris Zones를 통합해서 다른 컨테이너 포맷도 지원할 예정이다.