배경
기다리고 기다리던 Docker 시간이다. 사실 최근 팀플을 하면서 컨테이너를 만들 일이 많았다. 근데 뭔가 이걸 공개적으로 Docker Hub에 올리기는 상당히 수치스러운 컨테이너들이기 때문에, 자체적으로 호스팅을 하는게 낫지 않을까 하는 생각을 하게 되었다. 그러던 중 기존에 알던 Private Registry 등록을 실제로 한번 해보게 되었다. 절차는 복잡하지 않지만, 상당히 귀찮다.
참고 자료는:
- https://www.digitalocean.com/community/tutorials/how-to-set-up-a-private-docker-registry-on-ubuntu-22-04
- https://devopscube.com/create-self-signed-certificates-openssl/
- https://stackoverflow.com/questions/50768317/docker-pull-certificate-signed-by-unknown-authority
이렇게 3개를 참고했다.
Docker Compose 사용
Docker Compose는 참 간편하게 쓰기 좋은 것 같다. ~/docker-registry
디렉토리를 만들고, 여기에서 작업을 한다.
version: '3'
services:
registry:
image: registry:latest
ports:
- "5000:5000"
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry
REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password
volumes:
- ./auth:/auth
- ./data:/data
뭐 다들 알겠지만, 그냥 간단한 방법이다. 이따가 언급하겠지만, auth
디렉토리에는 로그인 정보를 저장할 예정이다. 그렇기에 추가적으로 auth
, data
디렉토리 두개를 만들어야한다. 그리고 나서는 귀찮은 작업이 시작된다.
Docker Registry 의 경우는 까다롭게 로그인 + HTTPS를 요구한다. 따라서 먼저 계정을 만들어주자. (물론 이걸 안하는 방법도 있지만, 그게 더 귀찮으니까 그냥 인증서 만드는게 편하다)
Nginx 설정
사실 :5000 으로 노출해서 사용해도 되긴 하나, 그러면 HTTPS 관련해서 귀찮은 이슈들이 많다. 그냥 그렇기에 Nginx의 reverse proxy를 사용해서 :5000를 노출하고, 그리고 나서 Nginx에다가 HTTPS를 붙여주는게 편하다.
먼저 HTTPS를 위해서 인증서를 생성하자. 만일 LetsEncrypt를 사용할 수 있는 상황이라면 그렇게 사용하면 된다. 하지만 내 환경은 지난번에 언급한 Private DNS를 운영중이기 때문에 LetsEncrypt를 사용할 수 없다. 따라서 자체 인증서를 발급해서 서명을 받아야 하는 번거로움이 존재한다.
~/docker-registry
디렉토리에서
mkdir openssl && cd openssl
openssl req -x509 \
-sha256 -days 356 \
-nodes \
-newkey rsa:2048 \
-subj "/CN=g-docker-reg.isu.mosl/C=US/L=San Fransisco" \
-keyout rootCA.key -out rootCA.crt
여기서 g-docker-reg.isu.mosl
이 도메인은 필요한 방식으로 바꿔주면 된다. 사실 뒤에 US랑 San Fransisco도 바꿔야 하는데 귀찮기 때문에 안바꿀 예정이다.
그리고 나서는 Private Key를 생성하고, csr.conf
를 만들어준다. 이후에 csr.conf
를 이용해서 인증서 Sign request를 만들 예정이다.
openssl genrsa -out server.key 2048
cat > csr.conf <<EOF
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[ dn ]
C = US
ST = California
L = San Fransisco
O = MLopsHub
OU = MlopsHub Dev
CN = g-docker-reg.isu.mosl
[ req_ext ]
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = g-docker-reg.isu.mosl
EOF
이렇게 설정하고 나서는 server.key
를 통해 config를 반영하여 server.csr
를 생성한다.
openssl req -new -key server.key -out server.csr -config csr.conf
그리고는 external file을 생성해준다.
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = g-docker-reg.isu.mosl
이후, Self signed CA를 통해서 SSL 인증서를 발급하자. 이러면 rootCA.key
를 생성할 수 있다.
openssl x509 -req \
-in server.csr \
-CA rootCA.crt -CAkey rootCA.key \
-CAcreateserial -out server.crt \
-days 365 \
-sha256 -extfile cert.conf
이러면 일단 필요한 server.crt
그리고 server.key
가 생성되었다. 그리고 이후에 rootCA.key
는 Docker config에다가 넣어줄 예정이니까, 지우지 말고 다 잘 간직해야한다.
이후 Nginx에다가 HTTPS 설정해준다.
server {
listen 443;
ssl on;
ssl_certificate /home/isu/docker-registry/openssl/server.crt;
ssl_certificate_key /home/isu/docker-registry/openssl/server.key;
server_name g-docker-reg.isu.mosl;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
return 404;
}
proxy_pass http://localhost:5000;
proxy_set_header Host $http_host; # required for docker client's sake
proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}
}
이런식으로 HTTPS 설정을 해준다. 방금 발급받은 server.crt
그리고 server.key
를 여기에 등록해주고, /
location에다가 Docker registry 의 reverse proxy를 설정한다. 이후 저장하고는 nginx -t
같은 명령어로 잘 설정이 되었는지 확인한다.
여기까지 성공했다면, 귀찮은 부분이 많이 없어진다. HTTPS를 설정했기에 docker pull
, docker push
같은 명령어가 문제 없이 실행될 것이다. 단, 다음의 단계까지 거쳐야한다.
로그인 설정
우리가 기존의 docker login
을 할 때 사용하는 것 처럼, 아이디 그리고 비밀번호를 만들어줘야한다. apache2-utils
를 사용해서 만들어준다.
sudo apt install apache2-utils -y
mkdir ~/docker-registry/auth
cd ~/docker-registry/auth
htpasswd -Bc registry.password isu
이러면 비밀번호를 입력해야한다. 입력을 하면, 잘 암호화 되어서 auth 디렉토리에 저장이 될 것이다. 그리고 나서는 드디어 docker compose up
을 사용할 수 있다.
추가 설정
docker login https://g-docker-reg.isu.mosl
이렇게 하면 로그인을 할 수 없다. 분명 다음의 에러가 뜰 것이다.
x509: certificate signed by unknown authority
이건 인증서 문제 때문에 생기는 것인데, 수동으로 Docker 내부에다가 인증서를 집어넣어주자.
/etc/docker/certs.d/g-docker-reg.isu.mosl/
이 디렉토리를 만들고, g-docker-reg.isu.mosl
은 알아서 바꿔서 적으면 된다. 저기에다가 아까 생성한 rootCA.crt
를 집어넣어준다. 저건 Docker Registry 그리고 저 레지스트리에서 pull, push를 할 모든 호스트에 집어넣어주면 된다. 귀찮지만, 그렇게 설정하면 잘 로그인이 된다.
테스트
docker build -t g-docker-reg.isu.mosl/g-frontend:latest .
가지고 있던 Dockerfile을 기반으로 만들어보았다. 앞의 레지스트리 g-docker-reg.isu.mosl
를 붙여주고, 그리고 g-frontend
라는 컨테이너 이름을 붙여준다. 그리고 나서는 push를 해보면 잘 되는걸 확인할 수 있다.
sudo docker push g-docker-reg.isu.mosl/g-frontend
Using default tag: latest
The push refers to repository [g-docker-reg.isu.mosl/g-frontend]
3e1ee9a7c418: Pushed
aecf7936a8b0: Pushed
8c6dcc0808e4: Pushed
de04641b39ef: Pushed
3c2345c37ae3: Pushed
ad81e0071736: Pushed
e36a529e2fd5: Pushed
0582abdc0475: Pushed
0cd3369ed144: Pushed
f55e8b0f1970: Pushed
76ce09dad18e: Pushed
9aee2e50701e: Pushed
678c62bc4ece: Pushed
d05b8af4c7ce: Pushed
latest: digest: sha256:981ba4bf86a0e7cc770a8059cf11ddc7900a33f2ac249f58bc8bc4da41bce704 size: 3261
결론
물론, rootCA.crt
를 다 복사해주는게 너무 귀찮기는 하다. 하지만 그래도 공개로 돌리기 조금 그런 컨테이너들을 잘 숨길 수 있을 것 같다.