[docker] 도커 사용방법

컨테이너 띄워보기

사용법

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

컨테이너 실행하기

docker run -it -d -p 8080:80 php:8.1-apache

위 명령어를 실행하면 도커는 php:8.1-apache 이미지가 없다면 pull 받아 컨테이너를 실행함

  • -it : -i옵션과 -t옵션을 같이 쓰이는 경우가 많음
    컨테이너를 실행한 후 컨테이너의 쉘이나 cli도구를 사용할때 쓰임
  • -d : 컨테이너를 백그라운드 모드로 실행
  • -p : 포트바인딩 local : containere
  • php:8.1-apache 컨테이너에 사용할 도커이미지
  • docker run | Docker Documentation

생성된 컨테이너 확인

docker ps  

컨테이너 자세히보기

docker inspect <CONTAINER ID>

컨테이너 이미지 빌드하기

  1. Dockerfile 작성
FROM php:8.1-apache
ENV FOO=bar 
WORKDIR /var/www/html
COPY . .
RUN apt-get update && install -y vim
  1. 이미지 빌드하기
    사용법
docker build [OPTIONS] PATH | URL | -

빌드하기

docker build -t myphp .

Dockerfile 수정이 있으면 위 명령어를 통해서 다시 빌드하면 됨

  • -t 이미지 태그 설정

빌드된 이미지 확인

docker image ls
  1. 컨테이너 생성하기
docker run -it -d --name my_php -p 8080:80  myphp
  • --name 옵션이 없는 경우 컨테이너 이름 랜덤 생성됨

컨테이너 터미널접속하기

docker exec -it <CONTAINER ID> /bin/bash

docker volume

컨테이너에서 생성한 파일은 컨테이너를 삭제하게 되면 같이 삭제 되므로 로컬저장소와 컨테이너저장소를 바인딩할 필요가 있다.

볼륨을 관리하는 방법 : Use bind mounts | Docker Documentation

  1. Named vlolumes
    사용법
docker volume create [OPTIONS] [VOLUME]

볼륨생성

docker volume create myVolume

볼륨마운트

docker run -dp 8080:80 -w /var/www/html -v "myVolume:/var/www/html" php:8.1-apache 

불륨정보확인

docker volume inspect myVolume
{
        "CreatedAt": "2021-12-08T14:16:18Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/myVolume/_data",
        "Name": "myVolume",
        "Options": {},
        "Scope": "local"
    }

볼륨정보를 보면 Mountpoint 에 지정된 경로가 컨테이너에 마운트되는 것을 확인 할수 있다.

Ubuntu, centos 환경에서 /var/lib/docker/volumes/ 에 접근할 수있지만 맥환경에서는 해당 경로가 존재하지 않는다.

맥에서 도커는 가상머신을 사용하기 때문이라는것 같은데 Mountpoint 접근 방법과 자세한 내용은 아래 링크를 참조

Mac에서 docker volume 위치

  1. Bind mounts
    불륨생성할 필요 없이 마운트할 경로를 입력해준다.
docker run -dp 8080:80 -w /var/www/html -v "your/path:/var/www/html" php:8.1-apache 

networks

사용법

docker network create [OPTIONS] NETWORK

네트워크생성

docker network create myphp

네트워크를 연결한 Mysql 컨테이너생성

docker run -d \
--network myphp --network-alias mysql \
-v myVolume:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=myphp \
mysql:5.7

mysql 컨테이너 실행

docker exec -it <mysql-container-id> mysql -u root -p

네트워크를 연결한 php컨테이너 mysql 연결하기

 docker run -dp 8080:80 \
   -w /var/www/html -v "$(pwd):/var/www/html" \
   --network myphp \
   -e MYSQL_HOST=mysql \
   -e MYSQL_USER=root \
   -e MYSQL_PASSWORD=secret \
   -e MYSQL_DB=myphp \
   php:8.1-apache 
docker exec -it <myphp-container-id> /bin/bash
  • MYSQL_HOST=mysql : --network-alias 를 mysql 로 지정했기 때문에 호스트를 네트워크별칭으로 넣어줌
dig MYSQL_HOST
; <<>> DiG 9.16.22-Debian <<>> mysql
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30839
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;mysql.                IN  A

;; ANSWER SECTION:
mysql.            600 IN  A   172.18.0.2

;; Query time: 1 msec
;; SERVER: 127.0.0.11#53(127.0.0.11)
;; WHEN: Wed Dec 08 15:07:17 UTC 2021
;; MSG SIZE  rcvd: 44

Mysql 컨테이너와 myphp컨테이너에서
dig mysql 명령어를 실행해보면 각각의 컨테이너가 생성한 네크워크를 사용하는 것을 확인 할 수 있다.

myphp 컨테이너에서는 MYSQL_HOST 에 네트워크 별칭을 선언했으므로 dig MYSQL_HOST 으로도 사용 가능하다.

docker compose 사용하기

1개의 서비스에 사용되는 컨테이너가 여러개이고 각각의 옵션이 복잡해진다면 이를 관리하기 힘들어 질것이다.

docker compose 를 사용하면 이러한 문제를 쉽게 해결 할수 있다.

설치확인

docker-compose version

설치문서 : Install Docker Compose | Docker Documentation
맥과 윈도우 환경에서는 별도의 설치가 필요 없다. 만약 docker compose 명령어를 존재하지 않는다는 메시지가 보인다면 루트권한으로 실행하면 된다.

Compose file 생성

  1. 루트 디렉토리에 docker-compose.yml 을 생성해야한다.
  2. 파일을 열고 버전을 명시해준다.
version: "3.7"
  1. 다음으로 사용할 서비스의 목록을 정의한다.
services:

서비스 정의
앞서 네트워스를 설명했던 2개의 커맨드를 docker-compose.yml 에 정의를 할것이다.

우선 두개의 명령어를 가져와 보자면 아래와 같다.
php

 docker run -dp 8080:80 \
   -w /var/www/html -v "$(pwd):/var/www/html" \
   --network myphp \
   -e MYSQL_HOST=mysql \
   -e MYSQL_USER=root \
   -e MYSQL_PASSWORD=secret \
   -e MYSQL_DB=myphp \
   php:8.1-apache 

mysql

docker run -d \
--network myphp --network-alias mysql \
-v myVolume:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=myphp \
mysql:5.7

파일작성하기

version: "3.7"

services:
  myphp:
    build:
      context: . # Dockerfile 경로
    container_name: php-web # 컨테이너 이름
    networks:
      mysql: #network aliase
    ports:
      - 8080:80
      - 8081:443
    volumes:
      - ./web_data:/var/www/html
    environment:
      - MYSQL_HOST=mysql #mysql is network aliase
      - MYSQL_USER=root
      - MYSQL_PASSWORD=secret
      - MYSQL_DB=myphp
  mysql:
    image: mysql:5.7
    container_name: php-mysql
    networks:
      mysql:
    volumes:
      - myVolume:/var/lib/mysql/
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_DATABASE=myphp

volumes:
  myVolume:

networks:
  mysql: #network aliase
    driver: bridge
    name: myphp #netwrok name (docker network ls 로 확인됨)

네트워크대신 link 지정하기
myphp 컨테이너와 mysql 컨테이너는 생성한 mysql 네트워크를 이용해서 서로 접근이 가능하게 처리했다.

이를 좀더 간단하게 처리하려면 컨테이너를 연결하는 link 옵션을 추가하면된다.

version: "3.7"

services:
  myphp:
    build:
      context: . # Dockerfile 경로
    container_name: php-web # 컨테이너 이름
    ports:
      - 8080:80
      - 8081:443
    volumes:
      - ./web_data:/var/www/html
    environment:
      - MYSQL_HOST=mysql #mysql is network aliase
      - MYSQL_USER=root
      - MYSQL_PASSWORD=secret
      - MYSQL_DB=myphp
    links:
      - mysql
  mysql:
    image: mysql:5.7
    container_name: php-mysql
    volumes:
      - myVolume:/var/lib/mysql/
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_DATABASE=myphp

volumes:
  myVolume:

mysql 접속 예제

$host='mysql';
$user='root';
$password='secret';
$dbname='myphp';
$mysqli = new mysqli($host, $user, $password, $dbname);
var_dump($mysqli);
  • $host='mysql'
    • network 를 사용한 경우 컨테이너 아이피 대신 network aliase 를 지정해서 사용할수 있음
    • link를 사용한 경우 컨테이너 아이피 대신 서비스 이름을 지정해서 사용할수 있음

컨테이너 고정아이피 지정하기
컨테이너에 고정아피를 사용해야하는 경우에는 어떻게 할까 ?
네트워크 설정에 ipam 를 정의한 뒤 각 서비스에 고정 아이피를 할당해주면 된다.

version: "3.7"

services:
  myphp:
    build:
      context: . # Dockerfile 경로
    container_name: php-web # 컨테이너 이름
    networks:
      mysql: #network aliase
        ipv4_address: 172.20.0.2
    ports:
      - 8080:80
      - 8081:443
    volumes:
      - ./web_data:/var/www/html
    environment:
      - MYSQL_HOST=mysql #mysql is network aliase
      - MYSQL_USER=root
      - MYSQL_PASSWORD=secret
      - MYSQL_DB=myphp
  mysql:
    image: mysql:5.7
    container_name: php-mysql
    networks:
      mysql:
        ipv4_address: 172.20.0.3
    volumes:
      - myVolume:/var/lib/mysql/
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_DATABASE=myphp

volumes:
  myVolume:

networks:
  mysql: #network aliase
    driver: bridge
    name: myphp #netwrok name (docker network ls 로 확인됨)
    ipam:
      config:
        - subnet: 172.20.0.0/24
          gateway: 172.20.0.1

변수사용하기
루트 디렉토리에 .env을 추가후 변수를 정의하면 docker-compose.yml 에서 사용할수 있다.

도커파일 디렉토리구조

  • project dir
    • Dockerfile
    • docker-compose.yml
    • .env

.env

...
MYSQL_PASSWORD=yourpassword
...

변수의 사용

...
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_PASSWORD}
...

[php] imagick install

요구사항

ImageMagick 이 설치되어 있어야함

ImageMagick 설치

패키지리스트 업데이트

apt update

ImageMagicK 설치 확인

convert --version

ImageMagicK 및 라이브러리 설치

apt install -y imagemagick libmagickwand-dev libmagickcore-dev

imagick 설치와 모듈 연동

php 모듈 imagick 설치

git clone https://github.com/Imagick/imagick
cd imagick
phpize && ./configure
make
make install

php.ini 모듈 추가
extension=imagick.so

apachectl restart
php -m | grep imagick

[docker] 도커설치 방법

도커설치방법
Ubuntu, Centos, Mac, window 운영체제 별로 설치 법이 있으니 공식 문서를 참조
Install Docker Engine | Docker Documentatio

컨테이너에서 도커를 사용하고 싶을때
ubuntu 환경에서 도커를 설치한 환경을 구성해야 할때 로컬환경이 ubuntu가 아니라면 도커 컨테이너를 통해 환경을 구성할 수 있다.

  • 로컬 도커설치
    • ubuntu 컨테이너 실행
      • 컨테이너에 도커 설치

설치방법은 동일하지만 컨테이너를 실행할때 --privileged 옵션을 통해 권한을 획득한 privileged 모드로 실행 해야한다. 그렇지 않으면 service docker start 명령어를 사용해도 도커가 실행되지 않는다.

Dockerfile 작성
docker-ce 설치시 타임존 선택 옵션이 주어지는데 이미지를 빌드할때 옵션을 선택 할 수 없으므로 타임존을 미리 설정해야 한다.

FROM ubuntu:latest

ENV TZ=Asia/Seoul
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

RUN apt-get update
RUN apt-get install -y \
        ca-certificates \
        curl \
        gnupg \
        lsb-release

RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
RUN echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
      $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

RUN apt-get update
RUN apt-get install -y docker-ce docker-ce-cli containerd.io

이미지 빌드

docker build . -t ubuntu:docker

컨테이너 생성

docker run -it --privileged ubuntu:docker

docker demon 실행

root@~:/# service docker status
 * Docker is not running
root@~:/# service docker start
 * Starting Docker: docker   

[javascript] window.open POST 값 보내기

$(".btn").click(function(){
    var url = 'test.test/test';
    var form = $("form");
    var target = '타이틀 제목';
    window.open(url, target, "width=500,height=500,resizable=yes,toolbar=yes,menubar=yes,location=yes");

    form.attr('action', url);
    form.attr('target', target); // window.open 타이틀과 매칭 되어야함
    form.attr('method', 'post');

    form.append('<input type="text" name="mb_id" value="foo">'); // 동적으로 값을 추가할때
    form.submit();
});

[php] phpwkhtmltopdf 패키지 설치와 사용방법

PDF관련 여러 패키지 문서를 확인해 보고 간단히 테스트 해봤지만 ‘phpwkhtmltopdf’ 만한 패키지가 없는것 같다.

요구사항

phpwkhtmltopdf 패키지는 wkhtmltopdf 라이브러리를 의존 하고 있기 때문에 반드시 설치 해야한다

apt-get insall wkhtmltopdf 으로 설치하게 되면 phpwkhtmltopdf 패키지의 PDF 병합 기능을 사용 할수 없으므로 공식사이트에서 제공 하는 설치파일로 설치하는것이 좋다.

  • wkhtmltopdf 의존성 패키지 설치
apt-get update && apt-get install -y \
        libfreetype6-dev \
        libfontconfig \
        zlib1g \
        libxrender1 \
        libxext6 \
        libx11-6 \
        fontconfig \
        xfonts-75dpi \
        xfonts-base
  • wkhtmltopdf 설치
wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.buster_amd64.deb

dpkg -i 'wkhtmltox_0.12.6-1.buster_amd64.deb'
  • phpwkhtmltopdf 설치
composer require mikehaertl/phpwkhtmltopdf

사용방법

  • 기본사용
use mikehaertl\wkhtmlto\Pdf;

// You can pass a filename, a HTML string, an URL or an options array to the constructor
$pdf = new Pdf('/path/to/page.html');

// On some systems you may have to set the path to the wkhtmltopdf executable
// $pdf->binary = 'C:\...';

if (!$pdf->saveAs('/path/to/page.pdf')) {
    $error = $pdf->getError();
    // ... handle error here
}
  • 커버 & table of content & PDF 이어붙이기
use mikehaertl\wkhtmlto\Pdf;

$pdf = new Pdf;
$pdf->addPage('/path/to/page.html');
$pdf->addPage('<html>....</html>');
$pdf->addPage('http://www.example.com');

// Add a cover (same sources as above are possible)
$pdf->addCover('/path/to/mycover.html');

// Add a Table of contents
$pdf->addToc();

// Save the PDF
if (!$pdf->saveAs('/path/to/report.pdf')) {
    $error = $pdf->getError();
    // ... handle error here
}

// ... or send to client for inline display
if (!$pdf->send()) {
    $error = $pdf->getError();
    // ... handle error here
}

// ... or send to client as file download
if (!$pdf->send('report.pdf')) {
    $error = $pdf->getError();
    // ... handle error here
}

// ... or you can get the raw pdf as a string
$content = $pdf->toString();
  • 이미지 생성
use mikehaertl\wkhtmlto\Image;

// You can pass a filename, a HTML string, an URL or an options array to the constructor
$image = new Image('/path/to/page.html');
$image->saveAs('/path/to/page.png');

// ... or send to client for inline display
if (!$image->send()) {
    $error = $image->getError();
    // ... handle error here
}

// ... or send to client as file download
if (!$image->send('page.png')) {
    $error = $image->getError();
    // ... handle error here
}

PDF 한글이 깨지는 경우 한글 폰트를 설치하면 됨
링크 참고

apt-get install fontconfig
curl -o nanumfont.zip http://cdn.naver.com/naver/NanumFont/fontfiles/NanumFont_TTF_ALL.zip
unzip -d /usr/share/fonts/nanum nanumfont.zip
fc-cache -f -v

PDF 관련 패키지 리스트


바로 테스트하기 (도커 설치 필수)

https://github.com/jisung87kr/docker_phpwkhtmltopdf

[laravel]라라벨 서비스컨테이너

  1. 컨테이너 바인딩
    • 기본바인딩
      • 서비스컨테이너 바인딩
      • 서비스프로바이더 register() 에서 등록함
      • 일반클래스는 등록할 필요가 없으며 인터페이스 구현객체인 경우 등록해서 사용함
      • 인터페이스와 클로저를 파라미터로 전달하여 바인딩
      • 컨트롤러와 같은 클래스에서 바인딩한 인터페이스를 타입힌트로 지정하면 클로저에 정의한 구현체가 자동으로 의존성 주입됨
      • 예) PostController.php 생성자에 Transistor 인터페이스 타입힌트를 지정하면 구현체인 PodcastParser::class 주입
# AppServiceProvider.php

use App\Services\Transistor;
use App\Services\PodcastParser;

...
public function register()
{
    //
    $this->app->bind(Transistor::class, function ($app) {
        return new PodcastParser();
    });
}
...
# PostController.php

use App\Services\Transistor;
use App\Services\PodcastParser;

public function __construct(Transistor $transistor)
{
    $this->transistor = $transistor;
}

[php]php를 이용한 웹크롤링

의존성

composer.json

{
    "require": {
        "guzzlehttp/guzzle": "^7.0",
        "symfony/dom-crawler": "^5.3",
        "symfony/css-selector": "^5.3"
    }
}

요청과 응답페이지 요소탐색

use GuzzleHttp\Client;
use Symfony\Component\DomCrawler\Crawler;

$url = 'http://ujsstudio.com';
$client = new Client();
$res = $client->get($url);
$res = $res->getBody();
$html = (string)$res; // 문자열로 형변환
// dom 필터링
$crawler = new Crawler($html);
$nodeValues = $crawler->filter("#primary a")->each(function(Crawler $node, $i){
    return $node->attr('href');
});

페이지네이션 처리

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Pool;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use Symfony\Component\DomCrawler\Crawler;

$result = [];
$client = new Client();

// 페이지 조회 익명함수(제너레이터) 생성
$requests = function ($total) use ($client) {
    $uri = 'http://ujsstudio.com/page';
    for ($i = 0; $i < $total; $i++) {
        yield function() use ($client, $uri, &$i) {
            $uri = $uri.'/'.$i+1;
            return $client->getAsync($uri);
        };
    }
};

$pool = new Pool($client, $requests(10), [
        'concurrency' => 5,
        'fulfilled' => function (Response $response, $index) use (&$result) {
            $res = $response->getBody();
            $html = (string)$res;

            // 요소탐색
            $crawler = new Crawler($html);
            $nodeValues = $crawler->filter("#primary a")->each(function(Crawler $node, $i){
                return $node->attr('href');
            });

            $result[] = $nodeValues;
        },
        'rejected' => function (RequestException $reason, $index) {
            // this is delivered each failed request
        },
]);

$promise = $pool->promise();
$promise->wait();

[php]stdClass object배열로 변환

function arrayCastRecursive($array)
{
    if (is_array($array)) {
        foreach ($array as $key => $value) {
            if (is_array($value)) {
                $array[$key] = arrayCastRecursive($value);
            }
            if ($value instanceof stdClass) {
                $array[$key] = arrayCastRecursive((array)$value);
            }
        }
    }
    if ($array instanceof stdClass) {
        return arrayCastRecursive((array)$array);
    }
    return $array;
}

$result = arrayCastRecursive($stdObj);

[docker] redmine 구축하기

docker-compose.yml 작성
https://github.com/jisung87kr/docker-redmine

version: '3.2'

services:

  redmine:
    image: redmine
    restart: always
    ports:
      - 8080:3000
    environment:
      REDMINE_DB_MYSQL: db
      REDMINE_DB_PASSWORD: PASSWORD
      REDMINE_SECRET_KEY_BASE: supersecretkey
    
  db:
    image: mysql:5.7
    restart: always
    ports:
     - 3330:3306
    environment:
      MYSQL_ROOT_PASSWORD: PASSWORD
      MYSQL_DATABASE: redmine 
    volumes:
     - db_data:/var/lib/mysql
    command: #문자셋 지정
     - --character-set-server=utf8mb4
     - --collation-server=utf8mb4_unicode_ci

volumes:
  db_data:

포트개방

docker-comse.yml 에서 지정한 포트가 개방되어 있어야 한다.

apache 설정

<VirtualHost *:80>
  ServerName redmine.ujsstudio.com
  #ProxyRequests Off
  #ProxyPreserveHost On
  ProxyPass / http://localhost:8888/
  ProxyPassReverse / http://localhost:8888/
</VirtualHost>

역프록시 설정을 해서 지정한 포트로 리다이렉트 처리

참고