이 포스트는 Korea Azure User Group에서 진행하는 Docker/Container 스터디 그룹에 참여하며 작성했습니다.
스터디를 진행한 Katacoda 강좌: Docker & Containers/Orchestration
진행한 강의 목록
- Add Healthcheck for Containers
- Deploy Swarm Services with Compose v3
- Keeping Secrets with Docker Swarm
- Create Encrypted Overlay Network
스터디는 Katacoda의 화면을 참조하되, 실습은 제 개인 PC에서 VM을 구성해서 진행했습니다. 따라해볼 수 있는 것만 별도로 정리했습니다.
구성환경
- Windows 10 Pro 1809
- Hyper-V
- CentOS 7.6.1810 (Kernel 4.20.2-1.el7)
내용 정리
# 컨테이너의 상태 확인을 확인해보자.
[root@docker-master home]# vi Dockerfile # Dockerfile 작성
FROM katacoda/docker-http-server:health
HEALTHCHECK --timeout=1s --interval=1s --retries=3 \
CMD curl -s --fail http://localhost:80/ || exit 1
[root@docker-master home]# docker build -t http . # 이미지 빌드
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM katacoda/docker-http-server:health
health: Pulling from katacoda/docker-http-server
12b41071e6ce: Pull complete
fb1cef6edba2: Pull complete
1061ea2815dd: Pull complete
Digest: sha256:fee2132b14b4148ded82aacd8f06bdcb9efa535b4dfd2f1d88518996f4b2fb1d
Status: Downloaded newer image for katacoda/docker-http-server:health
---> 7f16ea0c8bd8
Step 2/2 : HEALTHCHECK --timeout=1s --interval=1s --retries=3 CMD curl -s --fail http://localhost:80/ || exit 1
---> Running in 9b7a7fa9ad4c
Removing intermediate container 9b7a7fa9ad4c
---> a5ee862276bd
Successfully built a5ee862276bd
Successfully tagged http:latest
[root@docker-master home]# docker run -d -p 80:80 --name srv http # 컨테이너 배포
ab3e150b6ed3ef0c84b2d4c98d978b9e62b20303e94b9cd82087e236947fac5a
[root@docker-master home]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ab3e150b6ed3 http "/app" 2 seconds ago Up 2 seconds (healthy) 0.0.0.0:80->80/tcp srv
[root@docker-master home]# curl localhost/unhealthy # unhealthy 입력
[root@docker-master home]# docker inspect --format "{{json .State.Health.Status}}" srv # health 확인시 unhealthy로 출력
"unhealthy"
[root@docker-master home]# curl localhost/healthy # healthy 입력
[root@docker-master home]# docker inspect --format "{{json .State.Health.Status}}" srv # health 확인시 healthy로 출력
"healthy"
[root@docker-master home]# docker rm -f $(docker ps -qa) # 컨테이너 삭제
ab3e150b6ed3
[root@docker-master home]# docker service create --name http --replicas 2 -p 80:80 http # swarm으로 컨테이너 2개 배포
image http:latest could not be accessed on a registry to record
its digest. Each node will access http:latest independently,
possibly leading to different nodes running different
versions of the image.
7c530pawpqfr409xouxhfriia
overall progress: 2 out of 2 tasks
1/2: running
2/2: running
verify: Service converged
[root@docker-master home]# curl localhost/unhealthy # unhealthy 입력
[root@docker-master home]# curl localhost # 확인시 컨테이너 확인
<h1>A healthy request was processed by host: 4912d0761092</h1>
# Docker-compose v3 부터 추가된 swarm으로 배포하기
[root@docker-master home]# vi docker-compose.yml # compoase 파일 작성ㄱ
version: "3"
services:
redis:
image: redis:alpine
volumes:
- db-data:/data
networks:
appnet1:
aliases:
- db
deploy:
placement:
constraints: [node.role == manager]
web:
image: katacoda/redis-node-docker-example
networks:
- appnet1
depends_on:
- redis
deploy:
mode: replicated
replicas: 2
labels: [APP=WEB]
resources:
limits:
cpus: '0.25'
memory: 512M
reservations:
cpus: '0.25'
memory: 512M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
update_config:
parallelism: 1
delay: 10s
failure_action: continue
monitor: 60s
max_failure_ratio: 0.3
placement:
constraints: [node.role == worker]
networks:
appnet1:
volumes:
db-data:
[root@docker-master home]# docker stack deploy --compose-file docker-compose.yml myapp # stack 으로 배포
Creating network myapp_appnet1
Creating service myapp_redis
Creating service myapp_web
[root@docker-master home]# docker stack ls # stack 이름과 서비스 수량 등 확인
NAME SERVICES ORCHESTRATOR
myapp 2 Swarm
[root@docker-master home]# docker stack services myapp # 서비스 배포 확인
ID NAME MODE REPLICAS IMAGE PORTS
2g9kla9ee58s myapp_web replicated 2/2 katacoda/redis-node-docker-example:latest
zfp56qncx34u myapp_redis replicated 1/1 redis:alpine
[root@docker-master home]# docker stack ps myapp # 컨테이너 확인
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
qv91fxfzfvaw myapp_web.1 katacoda/redis-node-docker-example:latest docker-node01 Running Running 3 minutes ago
mjag86tngwr5 myapp_redis.1 redis:alpine docker-master Running Running 3 minutes ago
i196ixaxrk4a myapp_web.2 katacoda/redis-node-docker-example:latest docker-node01 Running Running 3 minutes ago
[root@docker-master home]# docker ps # 실제 컨테이너 확인
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9cb8459bcb86 redis:alpine "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 6379/tcp myapp_redis.1.mjag86tngwr5fy5aw3tj1mce2
# 암호화된 정보로 컨테이너 배포하기
[root@docker-master home]# < /dev/urandom tr -dc A-Za-z0-9 | head -c64 > tokenfile # 랜덤 키 파일 생성
[root@docker-master home]# docker secret create deep_thought_answer_secure tokenfile # 토큰파일을 이용해서 swarm 에서 배포시 암호를 사용하도록 설정
kq5vdq9q44xzopjvt71g5drmn
[root@docker-master home]# echo "the_answer_is_42" | docker secret create lesssecure # 암호화 확인
Error response from daemon: rpc error: code = InvalidArgument desc = secret data must be larger than 0 and less than 512000 bytes
[root@docker-master home]# docker secret ls # 암호 목록 확인
ID NAME DRIVER CREATED UPDATED
kq5vdq9q44xzopjvt71g5drmn deep_thought_answer_secure 24 seconds ago 24 seconds ago
[root@docker-master home]# docker service create --name="redis" --secret="deep_thought_answer_secure" redis # redis를 암호화시켜 실행하기
d0zur8n1l7teo7ojbjecvmy6c
overall progress: 1 out of 1 tasks
1/1: running
verify: Service converged
[root@docker-master home]# docker exec $(docker ps --filter name=redis -q) ls -l /run/secrets # redis 내부에 기록된 파일 확인
total 4
-r--r--r-- 1 root root 64 Mar 25 01:06 deep_thought_answer_secure
[root@docker-master home]# docker exec $(docker ps --filter name=redis -q) cat /run/secrets/deep_thought_answer_secure
qOl6w2bo6cVt0LHWcNvLBMvDd0IiroEFoi1PrLwKinOYfeFbeA77CqvqCGfJsEFt
[root@docker-master home]# vi docker-compose.yml # composw로 배포하기
version: '3.1'
services:
viewer:
image: 'alpine'
command: 'cat /run/secrets/deep_thought_answer_secure'
secrets:
- deep_thought_answer_secure
secrets:
deep_thought_answer_secure:
external: true
[root@docker-master home]# docker stack deploy -c docker-compose.yml secrets1 # compose를 읽어오도록 배포
Creating network secrets1_default
Creating service secrets1_viewer
[root@docker-master home]# echo "my-super-secure-cert" > secret.crt
[root@docker-master home]# vi docker-compose.yml
version: '3.1'
services:
test:
image: 'alpine'
command: 'cat /run/secrets/secretcert'
secrets:
- secretcert
secrets:
secretcert:
file: ./secret.crt
[root@docker-master home]# docker stack deploy -c docker-compose.yml secrets2
Creating network secrets2_default
Creating secret secrets2_secretcert
Creating service secrets2_test
[root@docker-master home]# docker logs $(docker ps -aqn1 -f name=secrets2 -f status=exited)
my-super-secure-cert
# swarm으로 배포시 이용하는 overlay 네트워크에 대해 암호화를 해보자.
# 2번째 노드에는 사전에 tcpdump를 설치한다.
[root@docker-node01 ~]# yum install tcpdump -y
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
epel/x86_64/metalink | 8.2 kB 00:00:00
* base: centos.mirror.cdnetworks.com
* elrepo: dfw.mirror.rackspace.com
* epel: ftp.cuhk.edu.hk
* extras: centos.mirror.cdnetworks.com
* updates: centos.mirror.moack.net
base | 3.6 kB 00:00:00
docker-ce-stable | 3.5 kB 00:00:00
elrepo | 2.9 kB 00:00:00
extras | 3.4 kB 00:00:00
updates | 3.4 kB 00:00:00
Resolving Dependencies
--> Running transaction check
---> Package tcpdump.x86_64 14:4.9.2-3.el7 will be installed
--> Finished Dependency Resolution
Dependencies Resolved
===============================================================================================================================================================================
Package Arch Version Repository Size
===============================================================================================================================================================================
Installing:
tcpdump x86_64 14:4.9.2-3.el7 base 421 k
Transaction Summary
===============================================================================================================================================================================
Install 1 Package
Total download size: 421 k
Installed size: 1.0 M
Downloading packages:
tcpdump-4.9.2-3.el7.x86_64.rpm | 421 kB 00:00:00
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
Installing : 14:tcpdump-4.9.2-3.el7.x86_64 1/1
Verifying : 14:tcpdump-4.9.2-3.el7.x86_64 1/1
Installed:
tcpdump.x86_64 14:4.9.2-3.el7
Complete!
[root@docker-master home]# docker network create -d overlay app1-network # 비암호화 overlay 네트워크 생성
stkvwxbp8ay7vz8ou3k1fx305
[root@docker-master home]# docker service create --name redis --network app1-network redis:alpine # 앞서 생성한 네트워크에 서비스 배포
--network app1-network -p 80:3000 \
--replicas 1 --name app1-web \
katacoda/redis-node-docker-example
7mn34bf8oxdgqoccelha8caqy
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
[root@docker-master home]# docker service create \
> --network app1-network -p 80:3000 \
> --replicas 1 --name app1-web \
> katacoda/redis-node-docker-example
wajhlj7csql6s0eabfu7rzul4
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
[root@docker-master home]# docker service ls # 서비스 실행 확인
ID NAME MODE REPLICAS IMAGE PORTS
wajhlj7csql6 app1-web replicated 1/1 katacoda/redis-node-docker-example:latest *:80->3000/tcp
7mn34bf8oxdg redis replicated 1/1 redis:alpine
[root@docker-master home]# curl localhost # 서비스 호출
This page was generated after talking to redis.
Application Build: 1
Total requests: 1
IP count:
::ffff:10.255.0.2: 1
# 2번째 노드에선 미리 tcpdump를 실행해둔다.
# master 노드에서 서비스 호출시 2번째 노드에 실행해둔 tcpdump에 암호화되지 않은 내용이 나오게 된다.
[root@docker-node01 ~]# tcpdump -s 1500 -A -i eth1 port 4789
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 1500 bytes
10:35:56.720049 IP docker-master.48187 > docker-node01.4789: VXLAN, flags [I] (0x08), vni 4096
IP 10.255.0.2.40488 > 10.255.0.21.http: Flags [S], seq 4078274696, win 65495, options [mss 65495,sackOK,TS val 3760921752 ecr 0,nop,wscale 7], length 0
E..n.m..@...
.
...;...Z...........B
....B
.....E..<..@.?.1.
...
....(.P............!..........
.+..........
10:35:56.720180 IP docker-node01.37479 > docker-master.4789: VXLAN, flags [I] (0x08), vni 4096
IP 10.255.0.21.http > 10.255.0.2.40488: Flags [S.], seq 3671223879, ack 4078274697, win 27960, options [mss 1410,sackOK,TS val 5536316 ecr 3760921752,nop,wscale 7], length 0
E..n1...@.4.
..
.
.g...Z...........B
....B
.....E..<..@.@.$.
...
....P.(..nG......m8.N.........
.Tz<.+......
10:35:56.720429 IP docker-master.48187 > docker-node01.4789: VXLAN, flags [I] (0x08), vni 4096
IP 10.255.0.2.40488 > 10.255.0.21.http: Flags [.], ack 1, win 512, options [nop,nop,TS val 3760921753 ecr 5536316], length 0
E..f.n..@...
.
......
.
...;...R...........B
....B
.....E..4..@.?.1.
...
....(.P......o^...........
.+...Tz@
# 암호화된 overlay 네트워크를 위해 앞서 생성한 서비스와 네트워크 삭제
[root@docker-master home]# docker service rm redis app1-web && docker network rm app1-network
redis
app1-web
app1-network
# 암호화된 overlay 네트워크 생성
[root@docker-master home]# docker network create -d overlay --opt encrypted app1-network
zggptayjp4wbdzn2k5j38k5m5
# 암호화된 overlay 네트워크로 서비스 배포
[root@docker-master home]# docker service create --name redis --network app1-network redis:alpine
docker service create \
--network app1-network -p 80:3000 \
--replicas 1 --name app1-web \
katacoda/redis-node-docker-example
pzaeni5zsymu5a1dx51un4pxu
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
[root@docker-master home]# docker service create \
> --network app1-network -p 80:3000 \
> --replicas 1 --name app1-web \
> katacoda/redis-node-docker-example
552ro33ut4thbcl60d5vahijf
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
[root@docker-master home]# curl localhost # 서비스 호출
This page was generated after talking to redis.
Application Build: 1
Total requests: 1
IP count:
::ffff:10.255.0.2: 1
# 앞서 마찬가지로 2번째 노드에는 tcpdump를 실행해둔다.
# master 노드에서 서비스 호출시 비암호화된 overlay 네트워크와 암호화된 overlay 네트워크간 나오는 정보의 양이 약 100여라인이 차이가 나게된다.
# 암호화된 overlay 네트워크에서 나오는 양이 100여라인이 더 적다.
[root@docker-node01 ~]# tcpdump -s 1500 -A -i eth1 port 4789
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 1500 bytes
10:39:22.478343 IP docker-master.52579 > docker-node01.4789: VXLAN, flags [I] (0x08), vni 4096
IP 10.255.0.2.40548 > 10.255.0.23.http: Flags [S], seq 4215025776, win 65495, options [mss 65495,sackOK,TS val 3761127511 ecr 0,nop,wscale 7], length 0
E..nO...@...
.
...c...Z...........B
....B
.....E..<.y@.?.v,
...
....d.P.<0p........M..........
..@W........
10:39:22.478481 IP docker-node01.39339 > docker-master.4789: VXLAN, flags [I] (0x08), vni 4096
IP 10.255.0.23.http > 10.255.0.2.40548: Flags [S.], seq 2836227324, ack 4215025777, win 27960, options [mss 1410,sackOK,TS val 5742074 ecr 3761127511,nop,wscale 7], length 0
E..n.c..@...
..
....
.
...c...R...........B
....B
.....E..4..@.?.v.
...
....d.P.<0...f............
..@\.W..