Installing Docker on Ubuntu
[https://docs.docker.com/engine/install/ubuntu/][https://docs.docker.com/engine/install/ubuntu/]

1. Install Docker

1.1 Installing Docker Community Edition on Ubuntu

이전 버전의 docker를 지워줍니다.

sudo apt-get remove docker docker-engine docker.io

HTTPS를 통해 repository를 다운받을수 있도록 관련 패키지를 설치합니다.

sudo apt-get install  apt-transport-https ca-certificates curl software-properties-common

gpg 키를 등록시킵니다.

sudo apt-get update
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

Repository를 설치후 Docker Community Edition을 설치합니다.

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

1.2 Docker Configuration

1.2.1 docker group

기본적으로 Docker Deamon은 TCP Port대신에 Unix Socket으로 연결이 됩니다. Unix Socket은 root에 소유된 것이므로, Docker를 실행하기 위해서는 항상 반드시 sudo로 해주어야 합니다.

sudo를 피하기 위해서는 docker group을 만들어주고, 여기에 users 를 등록시켜주면 됩니다.

sudo groupadd docker
sudo usermod -aG docker $USER

로그 아웃후 다시 로그인 합니다.
잘되는지 확인은 다음과 같이 합니다.

docker run hello-world

1.2.2 Purge AppArmor

container를 삭제하려고 할때 permission denied 에러가 나올수 있는데, 이 경우 AppArmor 를 삭제해주면 됩니다.

# status 체크
sudo aa-status

# Shutdown 시키고 restarting 금지 
sudo systemctl disable apparmor.service --now

# AppArmor 를 unload시킴
sudo service apparmor teardown

# 다시 상태 체크
sudo aa-status

1.2.3 iptables option

docker run -p port:port 또는 -P 옵션을 할때 docker는 iptables에서 해당 포트를 public으로 만듭니다.
이는 심지어 ufw default deny incoming 을 해도 ufw는 막지를 못합니다.
docker가 마음대로 iptalbes를 변경 못하도록 하기 위해서는 다음과 같이 옵션을 주면 됩니다.

sudo vi /etc/default/docker 
DOCKER_OPTS = "--iptables=false"

iptalbes에서 확인해봅니다.

sudo iptables -L -n --line-numbers | grep 3306
1    ACCEPT     tcp  --  0.0.0.0/0            172.17.0.2           tcp dpt:3306

다음과 같이 지울수 있습니다.

sudo iptables -D DOCKER 1

-D <체인이름> <헤더번호>

1.2.4 Enable UFW forwarding

Docker 는 기본적으로 container networking을 하기 위해서 bridge를 사용합니다.
문제는 ufw 는 모든 forwarding traffic을 drop시켜버립니다. 따라서 ufw의 forwarding policy를 설정해주어야 합니다.

또한 외부 host에서 docker container에 접속하기 위해서는 2376포트를 열어주어야 합니다. 2376포트는 Docker의 기본 포트입니다.

sudo vi /etc/default/ufw

다음을 DROP에서 ACCEPT로 바꿔줍니다.

DEFAULT_FORWARD_POLICY="ACCEPT"
sudo ufw reload
sudo ufw allow 2375/tcp

1.2.5 Configure Docker to start on boot

sudo systemctl enable docker

1.2.6 Install Ubuntu Libraries

Docker 내부에서..

apt-get update 
apt-get install -y sudo
apt-get install -y mercurial
apt-get install -y git
apt-get install -y python python-dev
apt-get install -y curl
apt-get install -y vim
apt-get install -y strace
apt-get install -y diffstat
apt-get install -y pkg-config
apt-get install -y cmake
apt-get install -y build-essential
apt-get install -y tcpdump
apt-get install -y screen
apt-get install -y man
apt-get install -y net-tools
apt-get install -y openssh-server
service ssh restart

2. Docker 101

2.1 Getting Started

docker info
docker pull ubuntu       

Images 보기

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              7                   328edcd84f1b        5 weeks ago         193MB

실행중이거나 중단된 containers보기

$ docker ps -a
CONTAINER ID    IMAGE    COMMAND        CREATED           STATUS                     PORTS    NAMES
3032c49eb6a3    centos   "/bin/bash"    11 seconds ago    Up 10 seconds                       tender_morse
8142605eda66    centos   "/bin/bash"    2 minutes ago     Exited (0) 2 minutes ago            centos

종료된 container 삭제시키기

$ docker rm 8142605eda66

2.2 Interactive Mode

  • --name [컨테이너 이름]
  • -i, --interactive
  • --user [유저이름]

기본적으로 root 계정으로 로그인한다

docker run --name dev -it ubuntu:20.04 /bin/bash

실행중인 container에 접속은 다음과 같이 합니다.

# 먼저 daemon으로 계속 실행하는 container를 생성
docker run -d --name daemon_ubuntu -t ubuntu:20.04 /bin/bash

# 실행중인 container에서 명령어 실행
docker exec -it daemon_ubuntu /bin/bash

2.3 VIM Ansi Mode

Docker 에서 vi를 실행시킬때 이상현상이 일어난다면 다음과 vim에서 다음과 같은 명령어를 쳐줍니다.
또는 .vimrc 파일안에 넣어도 됩니다.

:set term=builtin_ansi

또는 다음과 같이 합니다.

:set nocompatible

2.4 Delete all containers

docker rm $(docker ps -a -q)

# 또는 
docker container prune

2.5 Delete all images

docker rmi $(docker images -q)

2.6 Copy

Host File <–> Docker Container 복사하기

echo "Hello Anderson" > foo.txt
docker run -d --name mycontainer -t ubuntu:20.04 /bin/bash

docker cp foo.txt mycontainer:/foo.txt
docker exec -it mycontainer /bin/bash  # 들어가서 ls, cat으로 확인

docker cp mycontainer:/foo.txt foo.txt

2.7 Commit

docker run을 하게 되면 실제로는 새로운 container를 생성하게 됩니다.

$ docker run -d --name test_commit -t ubuntu:20.04 /bin/bash
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
e69589e2c14e        ubuntu:20.04        "/bin/bash"         27 seconds ago      Up 26 seconds                           test_commit

여기에서 CONTAINER ID를 잡아서 commit 시킵니다

docker commit test_commit andersonjo/test

2.8 Push

docker push andersonjo/test

만약 접속이 안되면 docker login 으로 해결.
잘됐으면 hub.docker.com 으로 들어가서 삭제 필요합니다.

2.9 Attach

Local standard input, output, error streams 를 running container에 붙입니다.
즉 실행중인 docker container의 로그를 볼 수 있습니다.

docker run -d --name topdemo ubuntu /usr/bin/top -b
docker attach topdemo

2.10 Inspect & IP Address

docker inspect <CONTAINER NAME>  | grep IPAddress

3. Dockerfile

3.1 Basic

Dockerfile 은 우선 FROM <이미지> 명령어부터 시작을 합니다.
`vi Dockerfile` 로 다음을 입력합니다.

FROM ubuntu:20.04
MAINTAINER Anderson Jo
USER root
WORKDIR ~/

RUN apt update
RUN apt install -y nginx
RUN echo "Hello Anderson!"
EXPOSE 22 80
CMD ["echo", "Hello! This is ANDERSON!"]

docker build -t [이미지 이름]를 하면 새로운 이미지가 생성이 됩니다.

docker build -t myubuntu .

이후에 docker run myubuntu 로 “Hello! This is ANDERSON!” 확인합니다.
docker run -p 9876:80 -d -it myubuntu nginx -g "daemon off;" 이것로 daemon 띄워놓고 크롬에서 확인합니다.
-g "daemon off;" 옵션은 nginx만 실행하면.. daemon으로 실행되는게 이것을 막기 위함입니다.

4. Networks

4.1 Basic Networks

docker network ls
NETWORK ID          NAME                DRIVER
adcf91619326        bridge              bridge              
80360f12ec4e        none                null                
15bd40f71d98        host                host 

위의 3가지 networks는 docker의 기본적인 네트워크입니다.

bridge 네트워크는 ifconfig를 치면 나오는 docker0 를 나타냅니다.
docker run –net= 이런식으로 network를 지정하지 않는한, 기본적으로 모든 container는 docker0에 붙습니다.

ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:d4:28:54:7e  
          inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:d4ff:fe28:547e/64 Scope:Link
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:23343 errors:0 dropped:0 overruns:0 frame:0
          TX packets:36673 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1502621 (1.5 MB)  TX bytes:48796224 (48.7 MB)

만약에 docker run –net=none 으로 시작할 경우 해당 docker는 network를 할 수 없습니다.
inspect를 통해서 자세한 정보를 얻을수 있습니다.

docker network inspect bridge

5. User defined networks

새로운 bridge network 또는 overlay network를 만들어서 containers를 isolate하는데 사용할수 있습니다. 또한 network plugin 그리고 remote network를 만들수도 있습니다.

5.1 Bridge Network

docker network create --driver bridge isolated_nw
docker run --net=<NETWORK>

–net 을 통해서 해당 network를 사용하도록 할 수 있습니다.
동일한 network를 공유하는 containers들 끼리는 서로 network communication이 가능합니다.

single host안에 상대적으로 작은 network를 구성시 bridge network가 좋습니다. 하지만 매우 큰 networks를 구성해야 한다면 overlay를 사용하는것이 좋습니다.

overlay, plugin 등등의 내용은 아래의 링크를 참조
docker networks guide

SSH Service

running ssh service

docker run -d -P --user ubuntu --name dev -h docker -t andersonjo/ubuntu bash

-P 는 모든 container의 ports를 hosts에 엽니다. (반드시 대문자)
container에 들어가서 netstat으로 port가 열려있는지 확인해봅니다.

docker exec -it dev bash
netstat -tln

모든 ports가 열려 있는지 확인합니다.

docker inspect dev  | grep PublishAllPorts
sudo apt-get install -y openssh-server
sudo service ssh restart
sudo sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config
sudo sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
echo "export VISIBLE=now" >> ~/.profile

MariaDB

docker pull mariadb
docker run --name mariadb -p 3306:3306 -e MYSQL_ROOT_PASSWORD=1234 -d mariadb
docker exec -it mariadb bash
apt-get update
apt-get install vim
vi /etc/mysql/my.cnf

run실행할때 -p 3306:3306 이부분이 중요합니다.
실질적으로 host 에서 3306 port mapping 시켜줍니다.

[mysqld_safe]
default-character-set=utf8

[mysqld]
collation-server = utf8_unicode_ci
init-connect='SET NAMES utf8'
character-set-server = utf8
docker restart mariadb
mysql -h 172.17.0.2 -u root -p

6. UFW Port Forward

6.1 Check Port Forwarding

sudo vi /etc/default/ufw
DEFAULT_FORWARD_POLICY="ACCEPT"
sudo vi /etc/ufw/sysctl.conf
net.ipv4.ip_forward=1
net/ipv6/conf/default/forwarding=1
net/ipv6/conf/all/forwarding=1

6.2 NAT

NAT를 ufw의 configuration에 넣어줍니다.
filter rules 전에 다음의 설정을 넣어줍니다.

sudo vi /etc/ufw/before.rules

# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]

# Forward traffic through eth0 - Change to match you out-interface
-A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE

# don't delete the 'COMMIT' line or these nat table rules won't
# be processed
COMMIT
sudo ufw disable && sudo ufw enable

6.3 Port Forwarding

# NAT table rules
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

# Port Forwardings
-A PREROUTING -i wlp2s0 -p tcp --dport 3306 --to-destination 172.17.0.1

# Forward traffic through eth0 - Change to match you out-interface
-A POSTROUTING -s 192.168.1.0/24 -o wlp2s0 -j MASQUERADE

# don't delete the 'COMMIT' line or these nat table rules won't
# be processed
COMMIT